diff options
Diffstat (limited to 'gcc/rust/checks')
89 files changed, 14564 insertions, 1174 deletions
diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/.cargo/config.toml b/gcc/rust/checks/errors/borrowck/ffi-polonius/.cargo/config.toml new file mode 100644 index 0000000..0236928 --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/.cargo/config.toml @@ -0,0 +1,5 @@ +[source.crates-io] +replace-with = "vendored-sources" + +[source.vendored-sources] +directory = "vendor" diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/Cargo.lock b/gcc/rust/checks/errors/borrowck/ffi-polonius/Cargo.lock new file mode 100644 index 0000000..f7cbd41 --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/Cargo.lock @@ -0,0 +1,39 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "datafrog" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0afaad2b26fa326569eb264b1363e8ae3357618c43982b3f285f0774ce76b69" + +[[package]] +name = "ffi-polonius" +version = "0.1.0" +dependencies = [ + "polonius-engine", +] + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "polonius-engine" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4e8e505342045d397d0b6674dcb82d6faf5cf40484d30eeb88fc82ef14e903f" +dependencies = [ + "datafrog", + "log", + "rustc-hash", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/src/gccrs_ffi.rs b/gcc/rust/checks/errors/borrowck/ffi-polonius/src/gccrs_ffi.rs index 0cb85078..7377e3a 100644 --- a/gcc/rust/checks/errors/borrowck/ffi-polonius/src/gccrs_ffi.rs +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/src/gccrs_ffi.rs @@ -30,11 +30,16 @@ // ``` include!("gccrs_ffi_generated.rs"); +use std::marker::{PhantomData, PhantomPinned}; + use crate::GccrsAtom; -// Using opqaue types -extern "C" { - pub type FFIVector; +// We define an opaque C type per the nomicon's recommendation: +// https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs +#[repr(C)] +pub struct FFIVector { + _empty: [u8; 0], + marker: PhantomData<(*mut u8, PhantomPinned)>, } impl<T1, T2> Into<(GccrsAtom, GccrsAtom)> for Pair<T1, T2> diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/src/lib.rs b/gcc/rust/checks/errors/borrowck/ffi-polonius/src/lib.rs index 782a63f..b21dee3 100644 --- a/gcc/rust/checks/errors/borrowck/ffi-polonius/src/lib.rs +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/src/lib.rs @@ -16,8 +16,6 @@ // along with GCC; see the file COPYING3. If not see // <http://www.gnu.org/licenses/>. -#![feature(extern_types)] - mod gccrs_ffi; use gccrs_ffi::FFIVector; @@ -107,9 +105,24 @@ impl From<gccrs_ffi::FactsView> for AllFacts<GccrsFacts> { fn print_point(point: GccrsAtom) { let val: usize = point.into(); + // Point is a 32 bit unsigned integer + // 16 15 1 + // xxxxxxxxxxxxxxxx xxxxxxxxxxxxxxx x + // ^~~~~~~~~~~~~~~~ ^~~~~~~~~~~~~~~ ^ + // | | | + // basic_block | start/mid + // statement + // the left most 16 bits store the basic block number + // the right most bit, represents the start/mid status + // the remaining 15 bits between these two represent the statement let mid = val % 2 == 1; let bb = val >> 16; - let stmt = (val >> 1) & ((1 << 15) - 1); + // firstly we can get rid of right most bit by performing left shift once + let hide_left_most_bit = val >> 1; + // now we only need the 15 bits on the right + // we can mask the remaining bits by performing bitwise AND with fifteen + // 1's which in hexadecimal is 0x7FFF + let stmt = hide_left_most_bit & 0x7FFF; eprint!("{}(bb{}[{}])", if mid { "Mid" } else { "Start" }, bb, stmt); } diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/datafrog/.cargo-checksum.json b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/datafrog/.cargo-checksum.json new file mode 100644 index 0000000..80aa32c --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/datafrog/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"CODE_OF_CONDUCT.md":"edca092fde496419a9f1ba640048aa0270b62dfea576cd3175f0b53e3c230470","Cargo.toml":"c3a8ecf831d7985fafcb8e523fd2d1bf875297e1a11b750a28222793a42e0d4c","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"60c181bf865b494df30968378509453719163f57a84f31a244fe69e62c342c5b","RELEASES.md":"a49128d725075bb614da3d53ea2aa2ab080bcb83ce46fc57655f6f6ecc9e2b74","examples/borrow_check.rs":"256857ed6609be8d1f3c8cf041ff8a1c0a884e8540f3156d2f3a2a2a9f73a05d","examples/graspan1.rs":"7d93ba71ff08a3667fea696d0a94e2c91e7514c304f2be8b088465cee17537fe","src/join.rs":"04eb29a02a1fd3ecf27d35a9eaabeec686bbfabdeafe13ad9ac98a622acb0f19","src/lib.rs":"7c95a63c237f48f986abd63ddfa4ed296bb5d280d245295d025fdf2f9744c2f3","src/map.rs":"93f1c7273fb67beb62a4b02201a6502bcaabf1e079aa7201a88d8e0aea6123e9","src/test.rs":"1eee5db2817a781cf8bf16744338b896252e400c150ae23ad87ce8c623acee69","src/treefrog.rs":"fe84a2bd2e36f1a48cb6b7e77a74addf218cfc881e9f6d4e7ceff4d8d97aa380"},"package":"a0afaad2b26fa326569eb264b1363e8ae3357618c43982b3f285f0774ce76b69"}
\ No newline at end of file diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/datafrog/CODE_OF_CONDUCT.md b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/datafrog/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..d70b2b5 --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/datafrog/CODE_OF_CONDUCT.md @@ -0,0 +1,40 @@ +# The Rust Code of Conduct + +A version of this document [can be found online](https://www.rust-lang.org/conduct.html). + +## Conduct + +**Contact**: [rust-mods@rust-lang.org](mailto:rust-mods@rust-lang.org) + +* We are committed to providing a friendly, safe and welcoming environment for all, regardless of level of experience, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, nationality, or other similar characteristic. +* On IRC, please avoid using overtly sexual nicknames or other nicknames that might detract from a friendly, safe and welcoming environment for all. +* Please be kind and courteous. There's no need to be mean or rude. +* Respect that people have differences of opinion and that every design or implementation choice carries a trade-off and numerous costs. There is seldom a right answer. +* Please keep unstructured critique to a minimum. If you have solid ideas you want to experiment with, make a fork and see how it works. +* We will exclude you from interaction if you insult, demean or harass anyone. That is not welcome behavior. We interpret the term "harassment" as including the definition in the <a href="http://citizencodeofconduct.org/">Citizen Code of Conduct</a>; if you have any lack of clarity about what might be included in that concept, please read their definition. In particular, we don't tolerate behavior that excludes people in socially marginalized groups. +* Private harassment is also unacceptable. No matter who you are, if you feel you have been or are being harassed or made uncomfortable by a community member, please contact one of the channel ops or any of the [Rust moderation team][mod_team] immediately. Whether you're a regular contributor or a newcomer, we care about making this community a safe place for you and we've got your back. +* Likewise any spamming, trolling, flaming, baiting or other attention-stealing behavior is not welcome. + +## Moderation + + +These are the policies for upholding our community's standards of conduct. If you feel that a thread needs moderation, please contact the [Rust moderation team][mod_team]. + +1. Remarks that violate the Rust standards of conduct, including hateful, hurtful, oppressive, or exclusionary remarks, are not allowed. (Cursing is allowed, but never targeting another user, and never in a hateful manner.) +2. Remarks that moderators find inappropriate, whether listed in the code of conduct or not, are also not allowed. +3. Moderators will first respond to such remarks with a warning. +4. If the warning is unheeded, the user will be "kicked," i.e., kicked out of the communication channel to cool off. +5. If the user comes back and continues to make trouble, they will be banned, i.e., indefinitely excluded. +6. Moderators may choose at their discretion to un-ban the user if it was a first offense and they offer the offended party a genuine apology. +7. If a moderator bans someone and you think it was unjustified, please take it up with that moderator, or with a different moderator, **in private**. Complaints about bans in-channel are not allowed. +8. Moderators are held to a higher standard than other community members. If a moderator creates an inappropriate situation, they should expect less leeway than others. + +In the Rust community we strive to go the extra step to look out for each other. Don't just aim to be technically unimpeachable, try to be your best self. In particular, avoid flirting with offensive or sensitive issues, particularly if they're off-topic; this all too often leads to unnecessary fights, hurt feelings, and damaged trust; worse, it can drive people away from the community entirely. + +And if someone takes issue with something you said or did, resist the urge to be defensive. Just stop doing what it was they complained about and apologize. Even if you feel you were misinterpreted or unfairly accused, chances are good there was something you could've communicated better — remember that it's your responsibility to make your fellow Rustaceans comfortable. Everyone wants to get along and we are all here first and foremost because we want to talk about cool technology. You will find that people will be eager to assume good intent and forgive as long as you earn their trust. + +The enforcement policies listed above apply to all official Rust venues; including official IRC channels (#rust, #rust-internals, #rust-tools, #rust-libs, #rustc, #rust-beginners, #rust-docs, #rust-community, #rust-lang, and #cargo); GitHub repositories under rust-lang, rust-lang-nursery, and rust-lang-deprecated; and all forums under rust-lang.org (users.rust-lang.org, internals.rust-lang.org). For other projects adopting the Rust Code of Conduct, please contact the maintainers of those projects for enforcement. If you wish to use this code of conduct for your own project, consider explicitly mentioning your moderation policy or making a copy with your own moderation policy so as to avoid confusion. + +*Adapted from the [Node.js Policy on Trolling](http://blog.izs.me/post/30036893703/policy-on-trolling) as well as the [Contributor Covenant v1.3.0](https://www.contributor-covenant.org/version/1/3/0/).* + +[mod_team]: https://www.rust-lang.org/team.html#Moderation-team diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/datafrog/Cargo.toml b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/datafrog/Cargo.toml new file mode 100644 index 0000000..71bccdd --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/datafrog/Cargo.toml @@ -0,0 +1,29 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g. crates.io) dependencies +# +# If you believe there's an error in this file please file an +# issue against the rust-lang/cargo repository. If you're +# editing this file be aware that the upstream Cargo.toml +# will likely look very different (and much more reasonable) + +[package] +edition = "2018" +name = "datafrog" +version = "2.0.1" +authors = ["Frank McSherry <fmcsherry@me.com>", "The Rust Project Developers", "Datafrog Developers"] +description = "Lightweight Datalog engine intended to be embedded in other Rust programs" +readme = "README.md" +keywords = ["datalog", "analysis"] +license = "Apache-2.0/MIT" +repository = "https://github.com/rust-lang-nursery/datafrog" +[dev-dependencies.proptest] +version = "0.8.7" +[badges.is-it-maintained-issue-resolution] +repository = "https://github.com/rust-lang-nursery/datafrog" + +[badges.is-it-maintained-open-issues] +repository = "https://github.com/rust-lang-nursery/datafrog" diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/datafrog/LICENSE-APACHE b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/datafrog/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/datafrog/LICENSE-APACHE @@ -0,0 +1,201 @@ + 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. diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/datafrog/LICENSE-MIT b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/datafrog/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/datafrog/LICENSE-MIT @@ -0,0 +1,23 @@ +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. diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/datafrog/README.md b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/datafrog/README.md new file mode 100644 index 0000000..9483584 --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/datafrog/README.md @@ -0,0 +1,44 @@ +# datafrog + +Datafrog is a lightweight Datalog engine intended to be embedded in other Rust programs. + +Datafrog has no runtime, and relies on you to build and repeatedly apply the update rules. +It tries to help you do this correctly. As an example, here is how you might write a reachability +query using Datafrog (minus the part where we populate the `nodes` and `edges` initial relations). + +```rust +extern crate datafrog; +use datafrog::Iteration; + +fn main() { + + // Create a new iteration context, ... + let mut iteration = Iteration::new(); + + // .. some variables, .. + let nodes_var = iteration.variable::<(u32,u32)>("nodes"); + let edges_var = iteration.variable::<(u32,u32)>("edges"); + + // .. load them with some initial values, .. + nodes_var.insert(nodes.into()); + edges_var.insert(edges.into()); + + // .. and then start iterating rules! + while iteration.changed() { + // nodes(a,c) <- nodes(a,b), edges(b,c) + nodes_var.from_join(&nodes_var, &edges_var, |_b, &a, &c| (c,a)); + } + + // extract the final results. + let reachable: Vec<(u32,u32)> = variable.complete(); +} +``` + +If you'd like to read more about how it works, check out [this blog post](https://github.com/frankmcsherry/blog/blob/master/posts/2018-05-19.md). + +## Authorship + +Datafrog was initially developed by [Frank McSherry][fmc] and was +later transferred to the rust-lang-nursery organization. Thanks Frank! + +[fmc]: https://github.com/frankmcsherry diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/datafrog/RELEASES.md b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/datafrog/RELEASES.md new file mode 100644 index 0000000..7d666f6 --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/datafrog/RELEASES.md @@ -0,0 +1,26 @@ +# 2.0.1 + +- Work around a rustdoc ICE (#24) + +# 2.0.0 + +- Breaking changes: + - leapjoin now takes a tuple of leapers, and not a `&mut` slice: + - `from_leapjoin(&input, &mut [&mut foo.extend_with(...), ..], ..)` becomes + `from_leapjoin(&input, (foo.extend_with(...), ..), ..)` + - if there is only one leaper, no tuple is needed + - `Relation::from` now requires a vector, not an iterator; use + `Relation::from_iter` instead +- Changed the API to permit using `Relation` and `Variable` more interchangeably, + and added a number of operations to construct relations directly, like `Relation::from_join` +- Extended leapfrog triejoin with new operations (`PrefixFilter` and `ValueFilter`) + +# 1.0.0 + +- Added leapfrog triejoin (#11). +- Have badges and repo links now! +- Minor performance improvements (#13). + +# 0.1.0 + +- Initial release. diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/datafrog/examples/borrow_check.rs b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/datafrog/examples/borrow_check.rs new file mode 100644 index 0000000..8f2197a --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/datafrog/examples/borrow_check.rs @@ -0,0 +1,115 @@ +extern crate datafrog; +use datafrog::Iteration; + +type Region = u32; +type Borrow = u32; +type Point = u32; + +fn main() { + let subset = { + // Create a new iteration context, ... + let mut iteration1 = Iteration::new(); + + // .. some variables, .. + let subset = iteration1.variable::<(Region, Region, Point)>("subset"); + + // different indices for `subset`. + let subset_r1p = iteration1.variable::<((Region, Point), Region)>("subset_r1p"); + let subset_r2p = iteration1.variable::<((Region, Point), Region)>("subset_r2p"); + let subset_p = iteration1.variable::<(Point, (Region, Region))>("subset_p"); + + // temporaries as we perform a multi-way join. + let subset_1 = iteration1.variable::<((Region, Point), Region)>("subset_1"); + let subset_2 = iteration1.variable::<((Region, Point), Region)>("subset_2"); + + let region_live_at = iteration1.variable::<((Region, Point), ())>("region_live_at"); + let cfg_edge_p = iteration1.variable::<(Point, Point)>("cfg_edge_p"); + + // load initial facts. + subset.insert(Vec::new().into()); + region_live_at.insert(Vec::new().into()); + cfg_edge_p.insert(Vec::new().into()); + + // .. and then start iterating rules! + while iteration1.changed() { + // remap fields to re-index by keys. + subset_r1p.from_map(&subset, |&(r1, r2, p)| ((r1, p), r2)); + subset_r2p.from_map(&subset, |&(r1, r2, p)| ((r2, p), r1)); + subset_p.from_map(&subset, |&(r1, r2, p)| (p, (r1, r2))); + + // R0: subset(R1, R2, P) :- outlives(R1, R2, P). + // Already loaded; outlives is static. + + // R1: subset(R1, R3, P) :- + // subset(R1, R2, P), + // subset(R2, R3, P). + subset.from_join(&subset_r2p, &subset_r1p, |&(_r2, p), &r1, &r3| (r1, r3, p)); + + // R2: subset(R1, R2, Q) :- + // subset(R1, R2, P), + // cfg_edge(P, Q), + // region_live_at(R1, Q), + // region_live_at(R2, Q). + + subset_1.from_join(&subset_p, &cfg_edge_p, |&_p, &(r1, r2), &q| ((r1, q), r2)); + subset_2.from_join(&subset_1, ®ion_live_at, |&(r1, q), &r2, &()| { + ((r2, q), r1) + }); + subset.from_join(&subset_2, ®ion_live_at, |&(r2, q), &r1, &()| (r1, r2, q)); + } + + subset_r1p.complete() + }; + + let _requires = { + // Create a new iteration context, ... + let mut iteration2 = Iteration::new(); + + // .. some variables, .. + let requires = iteration2.variable::<(Region, Borrow, Point)>("requires"); + requires.insert(Vec::new().into()); + + let requires_rp = iteration2.variable::<((Region, Point), Borrow)>("requires_rp"); + let requires_bp = iteration2.variable::<((Borrow, Point), Region)>("requires_bp"); + + let requires_1 = iteration2.variable::<(Point, (Borrow, Region))>("requires_1"); + let requires_2 = iteration2.variable::<((Region, Point), Borrow)>("requires_2"); + + let subset_r1p = iteration2.variable::<((Region, Point), Region)>("subset_r1p"); + subset_r1p.insert(subset); + + let killed = Vec::new().into(); + let region_live_at = iteration2.variable::<((Region, Point), ())>("region_live_at"); + let cfg_edge_p = iteration2.variable::<(Point, Point)>("cfg_edge_p"); + + // .. and then start iterating rules! + while iteration2.changed() { + requires_rp.from_map(&requires, |&(r, b, p)| ((r, p), b)); + requires_bp.from_map(&requires, |&(r, b, p)| ((b, p), r)); + + // requires(R, B, P) :- borrow_region(R, B, P). + // Already loaded; borrow_region is static. + + // requires(R2, B, P) :- + // requires(R1, B, P), + // subset(R1, R2, P). + requires.from_join(&requires_rp, &subset_r1p, |&(_r1, p), &b, &r2| (r2, b, p)); + + // requires(R, B, Q) :- + // requires(R, B, P), + // !killed(B, P), + // cfg_edge(P, Q), + // (region_live_at(R, Q); universal_region(R)). + + requires_1.from_antijoin(&requires_bp, &killed, |&(b, p), &r| (p, (b, r))); + requires_2.from_join(&requires_1, &cfg_edge_p, |&_p, &(b, r), &q| ((r, q), b)); + requires.from_join(&requires_2, ®ion_live_at, |&(r, q), &b, &()| (r, b, q)); + } + + requires.complete() + }; + + // borrow_live_at(B, P) :- requires(R, B, P), region_live_at(R, P) + + // borrow_live_at(B, P) :- requires(R, B, P), universal_region(R). +} diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/datafrog/examples/graspan1.rs b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/datafrog/examples/graspan1.rs new file mode 100644 index 0000000..31225b1 --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/datafrog/examples/graspan1.rs @@ -0,0 +1,62 @@ +extern crate datafrog; +use datafrog::Iteration; + +fn main() { + let timer = ::std::time::Instant::now(); + + // Make space for input data. + let mut nodes = Vec::new(); + let mut edges = Vec::new(); + + // Read input data from a handy file. + use std::fs::File; + use std::io::{BufRead, BufReader}; + + let filename = std::env::args().nth(1).unwrap(); + let file = BufReader::new(File::open(filename).unwrap()); + for readline in file.lines() { + let line = readline.expect("read error"); + if !line.is_empty() && !line.starts_with('#') { + let mut elts = line[..].split_whitespace(); + let src: u32 = elts.next().unwrap().parse().expect("malformed src"); + let dst: u32 = elts.next().unwrap().parse().expect("malformed dst"); + let typ: &str = elts.next().unwrap(); + match typ { + "n" => { + nodes.push((dst, src)); + } + "e" => { + edges.push((src, dst)); + } + unk => panic!("unknown type: {}", unk), + } + } + } + + println!("{:?}\tData loaded", timer.elapsed()); + + // Create a new iteration context, ... + let mut iteration = Iteration::new(); + + // .. some variables, .. + let variable1 = iteration.variable::<(u32, u32)>("nodes"); + let variable2 = iteration.variable::<(u32, u32)>("edges"); + + // .. load them with some initial values, .. + variable1.insert(nodes.into()); + variable2.insert(edges.into()); + + // .. and then start iterating rules! + while iteration.changed() { + // N(a,c) <- N(a,b), E(b,c) + variable1.from_join(&variable1, &variable2, |_b, &a, &c| (c, a)); + } + + let reachable = variable1.complete(); + + println!( + "{:?}\tComputation complete (nodes_final: {})", + timer.elapsed(), + reachable.len() + ); +} diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/datafrog/src/join.rs b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/datafrog/src/join.rs new file mode 100644 index 0000000..94270af --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/datafrog/src/join.rs @@ -0,0 +1,180 @@ +//! Join functionality. + +use super::{Relation, Variable}; +use std::cell::Ref; +use std::ops::Deref; + +/// Implements `join`. Note that `input1` must be a variable, but +/// `input2` can be either a variable or a relation. This is necessary +/// because relations have no "recent" tuples, so the fn would be a +/// guaranteed no-op if both arguments were relations. See also +/// `join_into_relation`. +pub(crate) fn join_into<'me, Key: Ord, Val1: Ord, Val2: Ord, Result: Ord>( + input1: &Variable<(Key, Val1)>, + input2: impl JoinInput<'me, (Key, Val2)>, + output: &Variable<Result>, + mut logic: impl FnMut(&Key, &Val1, &Val2) -> Result, +) { + let mut results = Vec::new(); + + let recent1 = input1.recent(); + let recent2 = input2.recent(); + + { + // scoped to let `closure` drop borrow of `results`. + + let mut closure = |k: &Key, v1: &Val1, v2: &Val2| results.push(logic(k, v1, v2)); + + for batch2 in input2.stable().iter() { + join_helper(&recent1, &batch2, &mut closure); + } + + for batch1 in input1.stable().iter() { + join_helper(&batch1, &recent2, &mut closure); + } + + join_helper(&recent1, &recent2, &mut closure); + } + + output.insert(Relation::from_vec(results)); +} + +/// Join, but for two relations. +pub(crate) fn join_into_relation<'me, Key: Ord, Val1: Ord, Val2: Ord, Result: Ord>( + input1: &Relation<(Key, Val1)>, + input2: &Relation<(Key, Val2)>, + mut logic: impl FnMut(&Key, &Val1, &Val2) -> Result, +) -> Relation<Result> { + let mut results = Vec::new(); + + join_helper(&input1.elements, &input2.elements, |k, v1, v2| { + results.push(logic(k, v1, v2)); + }); + + Relation::from_vec(results) +} + +/// Moves all recent tuples from `input1` that are not present in `input2` into `output`. +pub(crate) fn antijoin<'me, Key: Ord, Val: Ord, Result: Ord>( + input1: impl JoinInput<'me, (Key, Val)>, + input2: &Relation<Key>, + mut logic: impl FnMut(&Key, &Val) -> Result, +) -> Relation<Result> { + let mut tuples2 = &input2[..]; + + let results = input1 + .recent() + .iter() + .filter(|(ref key, _)| { + tuples2 = gallop(tuples2, |k| k < key); + tuples2.first() != Some(key) + }) + .map(|(ref key, ref val)| logic(key, val)) + .collect::<Vec<_>>(); + + Relation::from_vec(results) +} + +fn join_helper<K: Ord, V1, V2>( + mut slice1: &[(K, V1)], + mut slice2: &[(K, V2)], + mut result: impl FnMut(&K, &V1, &V2), +) { + while !slice1.is_empty() && !slice2.is_empty() { + use std::cmp::Ordering; + + // If the keys match produce tuples, else advance the smaller key until they might. + match slice1[0].0.cmp(&slice2[0].0) { + Ordering::Less => { + slice1 = gallop(slice1, |x| x.0 < slice2[0].0); + } + Ordering::Equal => { + // Determine the number of matching keys in each slice. + let count1 = slice1.iter().take_while(|x| x.0 == slice1[0].0).count(); + let count2 = slice2.iter().take_while(|x| x.0 == slice2[0].0).count(); + + // Produce results from the cross-product of matches. + for index1 in 0..count1 { + for s2 in slice2[..count2].iter() { + result(&slice1[0].0, &slice1[index1].1, &s2.1); + } + } + + // Advance slices past this key. + slice1 = &slice1[count1..]; + slice2 = &slice2[count2..]; + } + Ordering::Greater => { + slice2 = gallop(slice2, |x| x.0 < slice1[0].0); + } + } + } +} + +pub(crate) fn gallop<T>(mut slice: &[T], mut cmp: impl FnMut(&T) -> bool) -> &[T] { + // if empty slice, or already >= element, return + if !slice.is_empty() && cmp(&slice[0]) { + let mut step = 1; + while step < slice.len() && cmp(&slice[step]) { + slice = &slice[step..]; + step <<= 1; + } + + step >>= 1; + while step > 0 { + if step < slice.len() && cmp(&slice[step]) { + slice = &slice[step..]; + } + step >>= 1; + } + + slice = &slice[1..]; // advance one, as we always stayed < value + } + + slice +} + +/// An input that can be used with `from_join`; either a `Variable` or a `Relation`. +pub trait JoinInput<'me, Tuple: Ord>: Copy { + /// If we are on iteration N of the loop, these are the tuples + /// added on iteration N-1. (For a `Relation`, this is always an + /// empty slice.) + type RecentTuples: Deref<Target = [Tuple]>; + + /// If we are on iteration N of the loop, these are the tuples + /// added on iteration N - 2 or before. (For a `Relation`, this is + /// just `self`.) + type StableTuples: Deref<Target = [Relation<Tuple>]>; + + /// Get the set of recent tuples. + fn recent(self) -> Self::RecentTuples; + + /// Get the set of stable tuples. + fn stable(self) -> Self::StableTuples; +} + +impl<'me, Tuple: Ord> JoinInput<'me, Tuple> for &'me Variable<Tuple> { + type RecentTuples = Ref<'me, [Tuple]>; + type StableTuples = Ref<'me, [Relation<Tuple>]>; + + fn recent(self) -> Self::RecentTuples { + Ref::map(self.recent.borrow(), |r| &r.elements[..]) + } + + fn stable(self) -> Self::StableTuples { + Ref::map(self.stable.borrow(), |v| &v[..]) + } +} + +impl<'me, Tuple: Ord> JoinInput<'me, Tuple> for &'me Relation<Tuple> { + type RecentTuples = &'me [Tuple]; + type StableTuples = &'me [Relation<Tuple>]; + + fn recent(self) -> Self::RecentTuples { + &[] + } + + fn stable(self) -> Self::StableTuples { + std::slice::from_ref(self) + } +} diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/datafrog/src/lib.rs b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/datafrog/src/lib.rs new file mode 100644 index 0000000..d2f9323 --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/datafrog/src/lib.rs @@ -0,0 +1,567 @@ +//! A lightweight Datalog engine in Rust +//! +//! The intended design is that one has static `Relation` types that are sets +//! of tuples, and `Variable` types that represent monotonically increasing +//! sets of tuples. +//! +//! The types are mostly wrappers around `Vec<Tuple>` indicating sorted-ness, +//! and the intent is that this code can be dropped in the middle of an otherwise +//! normal Rust program, run to completion, and then the results extracted as +//! vectors again. + +#![forbid(missing_docs)] + +use std::cell::RefCell; +use std::cmp::Ordering; +use std::iter::FromIterator; +use std::rc::Rc; + +mod join; +mod map; +mod test; +mod treefrog; +pub use crate::join::JoinInput; +pub use crate::treefrog::{ + extend_anti::ExtendAnti, + extend_with::ExtendWith, + filter_anti::FilterAnti, + filter_with::FilterWith, + filters::{PrefixFilter, ValueFilter}, + Leaper, Leapers, RelationLeaper, +}; + +/// A static, ordered list of key-value pairs. +/// +/// A relation represents a fixed set of key-value pairs. In many places in a +/// Datalog computation we want to be sure that certain relations are not able +/// to vary (for example, in antijoins). +#[derive(Clone)] +pub struct Relation<Tuple: Ord> { + /// Sorted list of distinct tuples. + pub elements: Vec<Tuple>, +} + +impl<Tuple: Ord> Relation<Tuple> { + /// Merges two relations into their union. + pub fn merge(self, other: Self) -> Self { + let Relation { + elements: mut elements1, + } = self; + let Relation { + elements: mut elements2, + } = other; + + // If one of the element lists is zero-length, we don't need to do any work + if elements1.is_empty() { + return Relation { + elements: elements2, + }; + } + + if elements2.is_empty() { + return Relation { + elements: elements1, + }; + } + + // Make sure that elements1 starts with the lower element + // Will not panic since both collections must have at least 1 element at this point + if elements1[0] > elements2[0] { + std::mem::swap(&mut elements1, &mut elements2); + } + + // Fast path for when all the new elements are after the exiting ones + if elements1[elements1.len() - 1] < elements2[0] { + elements1.extend(elements2.into_iter()); + // println!("fast path"); + return Relation { + elements: elements1, + }; + } + + let mut elements = Vec::with_capacity(elements1.len() + elements2.len()); + let mut elements1 = elements1.drain(..); + let mut elements2 = elements2.drain(..).peekable(); + + elements.push(elements1.next().unwrap()); + if elements.first() == elements2.peek() { + elements2.next(); + } + + for elem in elements1 { + while elements2.peek().map(|x| x.cmp(&elem)) == Some(Ordering::Less) { + elements.push(elements2.next().unwrap()); + } + if elements2.peek().map(|x| x.cmp(&elem)) == Some(Ordering::Equal) { + elements2.next(); + } + elements.push(elem); + } + + // Finish draining second list + elements.extend(elements2); + + Relation { elements } + } + + /// Creates a `Relation` from the elements of the `iterator`. + /// + /// Same as the `from_iter` method from `std::iter::FromIterator` trait. + pub fn from_iter<I>(iterator: I) -> Self + where + I: IntoIterator<Item = Tuple>, + { + iterator.into_iter().collect() + } + + /// Creates a `Relation` using the `leapjoin` logic; + /// see [`Variable::from_leapjoin`] + pub fn from_leapjoin<'leap, SourceTuple: Ord, Val: Ord + 'leap>( + source: &Relation<SourceTuple>, + leapers: impl Leapers<'leap, SourceTuple, Val>, + logic: impl FnMut(&SourceTuple, &Val) -> Tuple, + ) -> Self { + treefrog::leapjoin(&source.elements, leapers, logic) + } + + /// Creates a `Relation` by joining the values from `input1` and + /// `input2` and then applying `logic`. Like + /// [`Variable::from_join`] except for use where the inputs are + /// not varying across iterations. + pub fn from_join<Key: Ord, Val1: Ord, Val2: Ord>( + input1: &Relation<(Key, Val1)>, + input2: &Relation<(Key, Val2)>, + logic: impl FnMut(&Key, &Val1, &Val2) -> Tuple, + ) -> Self { + join::join_into_relation(input1, input2, logic) + } + + /// Creates a `Relation` by removing all values from `input1` that + /// share a key with `input2`, and then transforming the resulting + /// tuples with the `logic` closure. Like + /// [`Variable::from_antijoin`] except for use where the inputs + /// are not varying across iterations. + pub fn from_antijoin<Key: Ord, Val1: Ord>( + input1: &Relation<(Key, Val1)>, + input2: &Relation<Key>, + logic: impl FnMut(&Key, &Val1) -> Tuple, + ) -> Self { + join::antijoin(input1, input2, logic) + } + + /// Construct a new relation by mapping another one. Equivalent to + /// creating an iterator but perhaps more convenient. Analogous to + /// `Variable::from_map`. + pub fn from_map<T2: Ord>(input: &Relation<T2>, logic: impl FnMut(&T2) -> Tuple) -> Self { + input.iter().map(logic).collect() + } + + /// Creates a `Relation` from a vector of tuples. + pub fn from_vec(mut elements: Vec<Tuple>) -> Self { + elements.sort(); + elements.dedup(); + Relation { elements } + } +} + +impl<Tuple: Ord> From<Vec<Tuple>> for Relation<Tuple> { + fn from(iterator: Vec<Tuple>) -> Self { + Self::from_vec(iterator) + } +} + +impl<Tuple: Ord> FromIterator<Tuple> for Relation<Tuple> { + fn from_iter<I>(iterator: I) -> Self + where + I: IntoIterator<Item = Tuple>, + { + Relation::from_vec(iterator.into_iter().collect()) + } +} + +impl<'tuple, Tuple: 'tuple + Copy + Ord> FromIterator<&'tuple Tuple> for Relation<Tuple> { + fn from_iter<I>(iterator: I) -> Self + where + I: IntoIterator<Item = &'tuple Tuple>, + { + Relation::from_vec(iterator.into_iter().cloned().collect()) + } +} + +impl<Tuple: Ord> std::ops::Deref for Relation<Tuple> { + type Target = [Tuple]; + fn deref(&self) -> &Self::Target { + &self.elements[..] + } +} + +/// An iterative context for recursive evaluation. +/// +/// An `Iteration` tracks monotonic variables, and monitors their progress. +/// It can inform the user if they have ceased changing, at which point the +/// computation should be done. +pub struct Iteration { + variables: Vec<Box<dyn VariableTrait>>, +} + +impl Iteration { + /// Create a new iterative context. + pub fn new() -> Self { + Iteration { + variables: Vec::new(), + } + } + /// Reports whether any of the monitored variables have changed since + /// the most recent call. + pub fn changed(&mut self) -> bool { + let mut result = false; + for variable in self.variables.iter_mut() { + if variable.changed() { + result = true; + } + } + result + } + /// Creates a new named variable associated with the iterative context. + pub fn variable<Tuple: Ord + 'static>(&mut self, name: &str) -> Variable<Tuple> { + let variable = Variable::new(name); + self.variables.push(Box::new(variable.clone())); + variable + } + /// Creates a new named variable associated with the iterative context. + /// + /// This variable will not be maintained distinctly, and may advertise tuples as + /// recent multiple times (perhaps unboundedly many times). + pub fn variable_indistinct<Tuple: Ord + 'static>(&mut self, name: &str) -> Variable<Tuple> { + let mut variable = Variable::new(name); + variable.distinct = false; + self.variables.push(Box::new(variable.clone())); + variable + } +} + +/// A type that can report on whether it has changed. +trait VariableTrait { + /// Reports whether the variable has changed since it was last asked. + fn changed(&mut self) -> bool; +} + +/// An monotonically increasing set of `Tuple`s. +/// +/// There are three stages in the lifecycle of a tuple: +/// +/// 1. A tuple is added to `self.to_add`, but is not yet visible externally. +/// 2. Newly added tuples are then promoted to `self.recent` for one iteration. +/// 3. After one iteration, recent tuples are moved to `self.tuples` for posterity. +/// +/// Each time `self.changed()` is called, the `recent` relation is folded into `tuples`, +/// and the `to_add` relations are merged, potentially deduplicated against `tuples`, and +/// then made `recent`. This way, across calls to `changed()` all added tuples are in +/// `recent` at least once and eventually all are in `tuples`. +/// +/// A `Variable` may optionally be instructed not to de-duplicate its tuples, for reasons +/// of performance. Such a variable cannot be relied on to terminate iterative computation, +/// and it is important that any cycle of derivations have at least one de-duplicating +/// variable on it. +pub struct Variable<Tuple: Ord> { + /// Should the variable be maintained distinctly. + distinct: bool, + /// A useful name for the variable. + name: String, + /// A list of relations whose union are the accepted tuples. + pub stable: Rc<RefCell<Vec<Relation<Tuple>>>>, + /// A list of recent tuples, still to be processed. + pub recent: Rc<RefCell<Relation<Tuple>>>, + /// A list of future tuples, to be introduced. + to_add: Rc<RefCell<Vec<Relation<Tuple>>>>, +} + +// Operator implementations. +impl<Tuple: Ord> Variable<Tuple> { + /// Adds tuples that result from joining `input1` and `input2` -- + /// each of the inputs must be a set of (Key, Value) tuples. Both + /// `input1` and `input2` must have the same type of key (`K`) but + /// they can have distinct value types (`V1` and `V2` + /// respectively). The `logic` closure will be invoked for each + /// key that appears in both inputs; it is also given the two + /// values, and from those it should construct the resulting + /// value. + /// + /// Note that `input1` must be a variable, but `input2` can be a + /// relation or a variable. Therefore, you cannot join two + /// relations with this method. This is not because the result + /// would be wrong, but because it would be inefficient: the + /// result from such a join cannot vary across iterations (as + /// relations are fixed), so you should prefer to invoke `insert` + /// on a relation created by `Relation::from_join` instead. + /// + /// # Examples + /// + /// This example starts a collection with the pairs (x, x+1) and (x+1, x) for x in 0 .. 10. + /// It then adds pairs (y, z) for which (x, y) and (x, z) are present. Because the initial + /// pairs are symmetric, this should result in all pairs (x, y) for x and y in 0 .. 11. + /// + /// ``` + /// use datafrog::{Iteration, Relation}; + /// + /// let mut iteration = Iteration::new(); + /// let variable = iteration.variable::<(usize, usize)>("source"); + /// variable.extend((0 .. 10).map(|x| (x, x + 1))); + /// variable.extend((0 .. 10).map(|x| (x + 1, x))); + /// + /// while iteration.changed() { + /// variable.from_join(&variable, &variable, |&key, &val1, &val2| (val1, val2)); + /// } + /// + /// let result = variable.complete(); + /// assert_eq!(result.len(), 121); + /// ``` + pub fn from_join<'me, K: Ord, V1: Ord, V2: Ord>( + &self, + input1: &'me Variable<(K, V1)>, + input2: impl JoinInput<'me, (K, V2)>, + logic: impl FnMut(&K, &V1, &V2) -> Tuple, + ) { + join::join_into(input1, input2, self, logic) + } + + /// Adds tuples from `input1` whose key is not present in `input2`. + /// + /// Note that `input1` must be a variable: if you have a relation + /// instead, you can use `Relation::from_antijoin` and then + /// `Variable::insert`. Note that the result will not vary during + /// the iteration. + /// + /// # Examples + /// + /// This example starts a collection with the pairs (x, x+1) for x in 0 .. 10. It then + /// adds any pairs (x+1,x) for which x is not a multiple of three. That excludes four + /// pairs (for 0, 3, 6, and 9) which should leave us with 16 total pairs. + /// + /// ``` + /// use datafrog::{Iteration, Relation}; + /// + /// let mut iteration = Iteration::new(); + /// let variable = iteration.variable::<(usize, usize)>("source"); + /// variable.extend((0 .. 10).map(|x| (x, x + 1))); + /// + /// let relation: Relation<_> = (0 .. 10).filter(|x| x % 3 == 0).collect(); + /// + /// while iteration.changed() { + /// variable.from_antijoin(&variable, &relation, |&key, &val| (val, key)); + /// } + /// + /// let result = variable.complete(); + /// assert_eq!(result.len(), 16); + /// ``` + pub fn from_antijoin<K: Ord, V: Ord>( + &self, + input1: &Variable<(K, V)>, + input2: &Relation<K>, + logic: impl FnMut(&K, &V) -> Tuple, + ) { + self.insert(join::antijoin(input1, input2, logic)) + } + + /// Adds tuples that result from mapping `input`. + /// + /// # Examples + /// + /// This example starts a collection with the pairs (x, x) for x in 0 .. 10. It then + /// repeatedly adds any pairs (x, z) for (x, y) in the collection, where z is the Collatz + /// step for y: it is y/2 if y is even, and 3*y + 1 if y is odd. This produces all of the + /// pairs (x, y) where x visits y as part of its Collatz journey. + /// + /// ``` + /// use datafrog::{Iteration, Relation}; + /// + /// let mut iteration = Iteration::new(); + /// let variable = iteration.variable::<(usize, usize)>("source"); + /// variable.extend((0 .. 10).map(|x| (x, x))); + /// + /// while iteration.changed() { + /// variable.from_map(&variable, |&(key, val)| + /// if val % 2 == 0 { + /// (key, val/2) + /// } + /// else { + /// (key, 3*val + 1) + /// }); + /// } + /// + /// let result = variable.complete(); + /// assert_eq!(result.len(), 74); + /// ``` + pub fn from_map<T2: Ord>(&self, input: &Variable<T2>, logic: impl FnMut(&T2) -> Tuple) { + map::map_into(input, self, logic) + } + + /// Adds tuples that result from combining `source` with the + /// relations given in `leapers`. This operation is very flexible + /// and can be used to do a combination of joins and anti-joins. + /// The main limitation is that the things being combined must + /// consist of one dynamic variable (`source`) and then several + /// fixed relations (`leapers`). + /// + /// The idea is as follows: + /// + /// - You will be inserting new tuples that result from joining (and anti-joining) + /// some dynamic variable `source` of source tuples (`SourceTuple`) + /// with some set of values (of type `Val`). + /// - You provide these values by combining `source` with a set of leapers + /// `leapers`, each of which is derived from a fixed relation. The `leapers` + /// should be either a single leaper (of suitable type) or else a tuple of leapers. + /// You can create a leaper in one of two ways: + /// - Extension: In this case, you have a relation of type `(K, Val)` for some + /// type `K`. You provide a closure that maps from `SourceTuple` to the key + /// `K`. If you use `relation.extend_with`, then any `Val` values the + /// relation provides will be added to the set of values; if you use + /// `extend_anti`, then the `Val` values will be removed. + /// - Filtering: In this case, you have a relation of type `K` for some + /// type `K` and you provide a closure that maps from `SourceTuple` to + /// the key `K`. Filters don't provide values but they remove source + /// tuples. + /// - Finally, you get a callback `logic` that accepts each `(SourceTuple, Val)` + /// that was successfully joined (and not filtered) and which maps to the + /// type of this variable. + pub fn from_leapjoin<'leap, SourceTuple: Ord, Val: Ord + 'leap>( + &self, + source: &Variable<SourceTuple>, + leapers: impl Leapers<'leap, SourceTuple, Val>, + logic: impl FnMut(&SourceTuple, &Val) -> Tuple, + ) { + self.insert(treefrog::leapjoin(&source.recent.borrow(), leapers, logic)); + } +} + +impl<Tuple: Ord> Clone for Variable<Tuple> { + fn clone(&self) -> Self { + Variable { + distinct: self.distinct, + name: self.name.clone(), + stable: self.stable.clone(), + recent: self.recent.clone(), + to_add: self.to_add.clone(), + } + } +} + +impl<Tuple: Ord> Variable<Tuple> { + fn new(name: &str) -> Self { + Variable { + distinct: true, + name: name.to_string(), + stable: Rc::new(RefCell::new(Vec::new())), + recent: Rc::new(RefCell::new(Vec::new().into())), + to_add: Rc::new(RefCell::new(Vec::new())), + } + } + + /// Inserts a relation into the variable. + /// + /// This is most commonly used to load initial values into a variable. + /// it is not obvious that it should be commonly used otherwise, but + /// it should not be harmful. + pub fn insert(&self, relation: Relation<Tuple>) { + if !relation.is_empty() { + self.to_add.borrow_mut().push(relation); + } + } + + /// Extend the variable with values from the iterator. + /// + /// This is most commonly used to load initial values into a variable. + /// it is not obvious that it should be commonly used otherwise, but + /// it should not be harmful. + pub fn extend<T>(&self, iterator: impl IntoIterator<Item = T>) + where + Relation<Tuple>: FromIterator<T>, + { + self.insert(iterator.into_iter().collect()); + } + + /// Consumes the variable and returns a relation. + /// + /// This method removes the ability for the variable to develop, and + /// flattens all internal tuples down to one relation. The method + /// asserts that iteration has completed, in that `self.recent` and + /// `self.to_add` should both be empty. + pub fn complete(self) -> Relation<Tuple> { + assert!(self.recent.borrow().is_empty()); + assert!(self.to_add.borrow().is_empty()); + let mut result: Relation<Tuple> = Vec::new().into(); + while let Some(batch) = self.stable.borrow_mut().pop() { + result = result.merge(batch); + } + result + } +} + +impl<Tuple: Ord> VariableTrait for Variable<Tuple> { + fn changed(&mut self) -> bool { + // 1. Merge self.recent into self.stable. + if !self.recent.borrow().is_empty() { + let mut recent = + ::std::mem::replace(&mut (*self.recent.borrow_mut()), Vec::new().into()); + while self + .stable + .borrow() + .last() + .map(|x| x.len() <= 2 * recent.len()) + == Some(true) + { + let last = self.stable.borrow_mut().pop().unwrap(); + recent = recent.merge(last); + } + self.stable.borrow_mut().push(recent); + } + + // 2. Move self.to_add into self.recent. + let to_add = self.to_add.borrow_mut().pop(); + if let Some(mut to_add) = to_add { + while let Some(to_add_more) = self.to_add.borrow_mut().pop() { + to_add = to_add.merge(to_add_more); + } + // 2b. Restrict `to_add` to tuples not in `self.stable`. + if self.distinct { + for batch in self.stable.borrow().iter() { + let mut slice = &batch[..]; + // Only gallop if the slice is relatively large. + if slice.len() > 4 * to_add.elements.len() { + to_add.elements.retain(|x| { + slice = join::gallop(slice, |y| y < x); + slice.is_empty() || &slice[0] != x + }); + } else { + to_add.elements.retain(|x| { + while !slice.is_empty() && &slice[0] < x { + slice = &slice[1..]; + } + slice.is_empty() || &slice[0] != x + }); + } + } + } + *self.recent.borrow_mut() = to_add; + } + + // let mut total = 0; + // for tuple in self.stable.borrow().iter() { + // total += tuple.len(); + // } + + // println!("Variable\t{}\t{}\t{}", self.name, total, self.recent.borrow().len()); + + !self.recent.borrow().is_empty() + } +} + +// impl<Tuple: Ord> Drop for Variable<Tuple> { +// fn drop(&mut self) { +// let mut total = 0; +// for batch in self.stable.borrow().iter() { +// total += batch.len(); +// } +// println!("FINAL: {:?}\t{:?}", self.name, total); +// } +// } diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/datafrog/src/map.rs b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/datafrog/src/map.rs new file mode 100644 index 0000000..1a8c101 --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/datafrog/src/map.rs @@ -0,0 +1,13 @@ +//! Map functionality. + +use super::{Relation, Variable}; + +pub(crate) fn map_into<T1: Ord, T2: Ord>( + input: &Variable<T1>, + output: &Variable<T2>, + logic: impl FnMut(&T1) -> T2, +) { + let results: Vec<T2> = input.recent.borrow().iter().map(logic).collect(); + + output.insert(Relation::from_vec(results)); +} diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/datafrog/src/test.rs b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/datafrog/src/test.rs new file mode 100644 index 0000000..9d5af35 --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/datafrog/src/test.rs @@ -0,0 +1,195 @@ +#![cfg(test)] + +use crate::Iteration; +use crate::Relation; +use crate::RelationLeaper; +use proptest::prelude::*; +use proptest::{proptest, proptest_helper}; + +fn inputs() -> impl Strategy<Value = Vec<(u32, u32)>> { + prop::collection::vec((0_u32..100, 0_u32..100), 1..500) +} + +/// The original way to use datafrog -- computes reachable nodes from a set of edges +fn reachable_with_var_join(edges: &[(u32, u32)]) -> Relation<(u32, u32)> { + let edges: Relation<_> = edges.iter().collect(); + let mut iteration = Iteration::new(); + + let edges_by_successor = iteration.variable::<(u32, u32)>("edges_invert"); + edges_by_successor.extend(edges.iter().map(|&(n1, n2)| (n2, n1))); + + let reachable = iteration.variable::<(u32, u32)>("reachable"); + reachable.insert(edges); + + while iteration.changed() { + // reachable(N1, N3) :- edges(N1, N2), reachable(N2, N3). + reachable.from_join(&reachable, &edges_by_successor, |&_, &n3, &n1| (n1, n3)); + } + + reachable.complete() +} + +/// Like `reachable`, but using a relation as an input to `from_join` +fn reachable_with_relation_join(edges: &[(u32, u32)]) -> Relation<(u32, u32)> { + let edges: Relation<_> = edges.iter().collect(); + let mut iteration = Iteration::new(); + + // NB. Changed from `reachable_with_var_join`: + let edges_by_successor: Relation<_> = edges.iter().map(|&(n1, n2)| (n2, n1)).collect(); + + let reachable = iteration.variable::<(u32, u32)>("reachable"); + reachable.insert(edges); + + while iteration.changed() { + // reachable(N1, N3) :- edges(N1, N2), reachable(N2, N3). + reachable.from_join(&reachable, &edges_by_successor, |&_, &n3, &n1| (n1, n3)); + } + + reachable.complete() +} + +fn reachable_with_leapfrog(edges: &[(u32, u32)]) -> Relation<(u32, u32)> { + let edges: Relation<_> = edges.iter().collect(); + let mut iteration = Iteration::new(); + + let edges_by_successor: Relation<_> = edges.iter().map(|&(n1, n2)| (n2, n1)).collect(); + + let reachable = iteration.variable::<(u32, u32)>("reachable"); + reachable.insert(edges); + + while iteration.changed() { + // reachable(N1, N3) :- edges(N1, N2), reachable(N2, N3). + reachable.from_leapjoin( + &reachable, + edges_by_successor.extend_with(|&(n2, _)| n2), + |&(_, n3), &n1| (n1, n3), + ); + } + + reachable.complete() +} + +/// Computes a join where the values are summed -- uses iteration +/// variables (the original datafrog technique). +fn sum_join_via_var( + input1_slice: &[(u32, u32)], + input2_slice: &[(u32, u32)], +) -> Relation<(u32, u32)> { + let mut iteration = Iteration::new(); + + let input1 = iteration.variable::<(u32, u32)>("input1"); + input1.extend(input1_slice); + + let input2 = iteration.variable::<(u32, u32)>("input1"); + input2.extend(input2_slice); + + let output = iteration.variable::<(u32, u32)>("output"); + + while iteration.changed() { + // output(K1, V1 * 100 + V2) :- input1(K1, V1), input2(K1, V2). + output.from_join(&input1, &input2, |&k1, &v1, &v2| (k1, v1 * 100 + v2)); + } + + output.complete() +} + +/// Computes a join where the values are summed -- uses iteration +/// variables (the original datafrog technique). +fn sum_join_via_relation( + input1_slice: &[(u32, u32)], + input2_slice: &[(u32, u32)], +) -> Relation<(u32, u32)> { + let input1: Relation<_> = input1_slice.iter().collect(); + let input2: Relation<_> = input2_slice.iter().collect(); + Relation::from_join(&input1, &input2, |&k1, &v1, &v2| (k1, v1 * 100 + v2)) +} + +proptest! { + #[test] + fn reachable_leapfrog_vs_var_join(edges in inputs()) { + let reachable1 = reachable_with_var_join(&edges); + let reachable2 = reachable_with_leapfrog(&edges); + assert_eq!(reachable1.elements, reachable2.elements); + } + + #[test] + fn reachable_rel_join_vs_var_join(edges in inputs()) { + let reachable1 = reachable_with_var_join(&edges); + let reachable2 = reachable_with_relation_join(&edges); + assert_eq!(reachable1.elements, reachable2.elements); + } + + #[test] + fn sum_join_from_var_vs_rel((set1, set2) in (inputs(), inputs())) { + let output1 = sum_join_via_var(&set1, &set2); + let output2 = sum_join_via_relation(&set1, &set2); + assert_eq!(output1.elements, output2.elements); + } + + /// Test the behavior of `filter_anti` used on its own in a + /// leapjoin -- effectively it becomes an "intersection" + /// operation. + #[test] + fn filter_with_on_its_own((set1, set2) in (inputs(), inputs())) { + let input1: Relation<(u32, u32)> = set1.iter().collect(); + let input2: Relation<(u32, u32)> = set2.iter().collect(); + let intersection1 = Relation::from_leapjoin( + &input1, + input2.filter_with(|&tuple| tuple), + |&tuple, &()| tuple, + ); + + let intersection2: Relation<(u32, u32)> = input1.elements.iter() + .filter(|t| input2.elements.binary_search(&t).is_ok()) + .collect(); + + assert_eq!(intersection1.elements, intersection2.elements); + } + + /// Test the behavior of `filter_anti` used on its own in a + /// leapjoin -- effectively it becomes a "set minus" operation. + #[test] + fn filter_anti_on_its_own((set1, set2) in (inputs(), inputs())) { + let input1: Relation<(u32, u32)> = set1.iter().collect(); + let input2: Relation<(u32, u32)> = set2.iter().collect(); + + let difference1 = Relation::from_leapjoin( + &input1, + input2.filter_anti(|&tuple| tuple), + |&tuple, &()| tuple, + ); + + let difference2: Relation<(u32, u32)> = input1.elements.iter() + .filter(|t| input2.elements.binary_search(&t).is_err()) + .collect(); + + assert_eq!(difference1.elements, difference2.elements); + } +} + +/// Test that `from_leapjoin` matches against the tuples from an +/// `extend` that precedes first iteration. +/// +/// This was always true, but wasn't immediately obvious to me until I +/// re-read the code more carefully. -nikomatsakis +#[test] +fn leapjoin_from_extend() { + let doubles: Relation<(u32, u32)> = (0..10).map(|i| (i, i * 2)).collect(); + + let mut iteration = Iteration::new(); + + let variable = iteration.variable::<(u32, u32)>("variable"); + variable.extend(Some((2, 2))); + + while iteration.changed() { + variable.from_leapjoin( + &variable, + doubles.extend_with(|&(i, _)| i), + |&(i, _), &j| (i, j), + ); + } + + let variable = variable.complete(); + + assert_eq!(variable.elements, vec![(2, 2), (2, 4)]); +} diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/datafrog/src/treefrog.rs b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/datafrog/src/treefrog.rs new file mode 100644 index 0000000..2ad238f --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/datafrog/src/treefrog.rs @@ -0,0 +1,661 @@ +//! Join functionality. + +use super::Relation; + +/// Performs treefrog leapjoin using a list of leapers. +pub(crate) fn leapjoin<'leap, Tuple: Ord, Val: Ord + 'leap, Result: Ord>( + source: &[Tuple], + mut leapers: impl Leapers<'leap, Tuple, Val>, + mut logic: impl FnMut(&Tuple, &Val) -> Result, +) -> Relation<Result> { + let mut result = Vec::new(); // temp output storage. + let mut values = Vec::new(); // temp value storage. + + for tuple in source { + // Determine which leaper would propose the fewest values. + let mut min_index = usize::max_value(); + let mut min_count = usize::max_value(); + leapers.for_each_count(tuple, |index, count| { + if min_count > count { + min_count = count; + min_index = index; + } + }); + + // We had best have at least one relation restricting values. + assert!(min_count < usize::max_value()); + + // If there are values to propose: + if min_count > 0 { + // Push the values that `min_index` "proposes" into `values`. + leapers.propose(tuple, min_index, &mut values); + + // Give other leapers a chance to remove values from + // anti-joins or filters. + leapers.intersect(tuple, min_index, &mut values); + + // Push remaining items into result. + for val in values.drain(..) { + result.push(logic(tuple, val)); + } + } + } + + Relation::from_vec(result) +} + +/// Implemented for a tuple of leapers +pub trait Leapers<'leap, Tuple, Val> { + /// Internal method: + fn for_each_count(&mut self, tuple: &Tuple, op: impl FnMut(usize, usize)); + + /// Internal method: + fn propose(&mut self, tuple: &Tuple, min_index: usize, values: &mut Vec<&'leap Val>); + + /// Internal method: + fn intersect(&mut self, tuple: &Tuple, min_index: usize, values: &mut Vec<&'leap Val>); +} + +macro_rules! tuple_leapers { + ($($Ty:ident)*) => { + #[allow(unused_assignments, non_snake_case)] + impl<'leap, Tuple, Val, $($Ty),*> Leapers<'leap, Tuple, Val> for ($($Ty,)*) + where + $($Ty: Leaper<'leap, Tuple, Val>,)* + { + fn for_each_count(&mut self, tuple: &Tuple, mut op: impl FnMut(usize, usize)) { + let ($($Ty,)*) = self; + let mut index = 0; + $( + let count = $Ty.count(tuple); + op(index, count); + index += 1; + )* + } + + fn propose(&mut self, tuple: &Tuple, min_index: usize, values: &mut Vec<&'leap Val>) { + let ($($Ty,)*) = self; + let mut index = 0; + $( + if min_index == index { + return $Ty.propose(tuple, values); + } + index += 1; + )* + panic!("no match found for min_index={}", min_index); + } + + fn intersect(&mut self, tuple: &Tuple, min_index: usize, values: &mut Vec<&'leap Val>) { + let ($($Ty,)*) = self; + let mut index = 0; + $( + if min_index != index { + $Ty.intersect(tuple, values); + } + index += 1; + )* + } + } + } +} + +tuple_leapers!(A B); +tuple_leapers!(A B C); +tuple_leapers!(A B C D); +tuple_leapers!(A B C D E); +tuple_leapers!(A B C D E F); +tuple_leapers!(A B C D E F G); + +/// Methods to support treefrog leapjoin. +pub trait Leaper<'leap, Tuple, Val> { + /// Estimates the number of proposed values. + fn count(&mut self, prefix: &Tuple) -> usize; + /// Populates `values` with proposed values. + fn propose(&mut self, prefix: &Tuple, values: &mut Vec<&'leap Val>); + /// Restricts `values` to proposed values. + fn intersect(&mut self, prefix: &Tuple, values: &mut Vec<&'leap Val>); +} + +pub(crate) mod filters { + use super::Leaper; + use super::Leapers; + + /// A treefrog leaper that tests each of the tuples from the main + /// input (the "prefix"). Use like `PrefixFilter::from(|tuple| + /// ...)`; if the closure returns true, then the tuple is + /// retained, else it will be ignored. This leaper can be used in + /// isolation in which case it just acts like a filter on the + /// input (the "proposed value" will be `()` type). + pub struct PrefixFilter<Tuple, Func: Fn(&Tuple) -> bool> { + phantom: ::std::marker::PhantomData<Tuple>, + predicate: Func, + } + + impl<'leap, Tuple, Func> PrefixFilter<Tuple, Func> + where + Func: Fn(&Tuple) -> bool, + { + /// Creates a new filter based on the prefix + pub fn from(predicate: Func) -> Self { + PrefixFilter { + phantom: ::std::marker::PhantomData, + predicate, + } + } + } + + impl<'leap, Tuple, Val, Func> Leaper<'leap, Tuple, Val> for PrefixFilter<Tuple, Func> + where + Func: Fn(&Tuple) -> bool, + { + /// Estimates the number of proposed values. + fn count(&mut self, prefix: &Tuple) -> usize { + if (self.predicate)(prefix) { + usize::max_value() + } else { + 0 + } + } + /// Populates `values` with proposed values. + fn propose(&mut self, _prefix: &Tuple, _values: &mut Vec<&'leap Val>) { + panic!("PrefixFilter::propose(): variable apparently unbound"); + } + /// Restricts `values` to proposed values. + fn intersect(&mut self, _prefix: &Tuple, _values: &mut Vec<&'leap Val>) { + // We can only be here if we returned max_value() above. + } + } + + impl<'leap, Tuple, Func> Leapers<'leap, Tuple, ()> for PrefixFilter<Tuple, Func> + where + Func: Fn(&Tuple) -> bool, + { + fn for_each_count(&mut self, tuple: &Tuple, mut op: impl FnMut(usize, usize)) { + if <Self as Leaper<'_, Tuple, ()>>::count(self, tuple) == 0 { + op(0, 0) + } else { + // we will "propose" the `()` value if the predicate applies + op(0, 1) + } + } + + fn propose(&mut self, _: &Tuple, min_index: usize, values: &mut Vec<&'leap ()>) { + assert_eq!(min_index, 0); + values.push(&()); + } + + fn intersect(&mut self, _: &Tuple, min_index: usize, values: &mut Vec<&'leap ()>) { + assert_eq!(min_index, 0); + assert_eq!(values.len(), 1); + } + } + + /// A treefrog leaper based on a predicate of prefix and value. + /// Use like `ValueFilter::from(|tuple, value| ...)`. The closure + /// should return true if `value` ought to be retained. The + /// `value` will be a value proposed elsewhere by an `extend_with` + /// leaper. + /// + /// This leaper cannot be used in isolation, it must be combined + /// with other leapers. + pub struct ValueFilter<Tuple, Val, Func: Fn(&Tuple, &Val) -> bool> { + phantom: ::std::marker::PhantomData<(Tuple, Val)>, + predicate: Func, + } + + impl<'leap, Tuple, Val, Func> ValueFilter<Tuple, Val, Func> + where + Func: Fn(&Tuple, &Val) -> bool, + { + /// Creates a new filter based on the prefix + pub fn from(predicate: Func) -> Self { + ValueFilter { + phantom: ::std::marker::PhantomData, + predicate, + } + } + } + + impl<'leap, Tuple, Val, Func> Leaper<'leap, Tuple, Val> for ValueFilter<Tuple, Val, Func> + where + Func: Fn(&Tuple, &Val) -> bool, + { + /// Estimates the number of proposed values. + fn count(&mut self, _prefix: &Tuple) -> usize { + usize::max_value() + } + /// Populates `values` with proposed values. + fn propose(&mut self, _prefix: &Tuple, _values: &mut Vec<&'leap Val>) { + panic!("PrefixFilter::propose(): variable apparently unbound"); + } + /// Restricts `values` to proposed values. + fn intersect(&mut self, prefix: &Tuple, values: &mut Vec<&'leap Val>) { + values.retain(|val| (self.predicate)(prefix, val)); + } + } + +} + +/// Extension method for relations. +pub trait RelationLeaper<Key: Ord, Val: Ord> { + /// Extend with `Val` using the elements of the relation. + fn extend_with<'leap, Tuple: Ord, Func: Fn(&Tuple) -> Key>( + &'leap self, + key_func: Func, + ) -> extend_with::ExtendWith<'leap, Key, Val, Tuple, Func> + where + Key: 'leap, + Val: 'leap; + /// Extend with `Val` using the complement of the relation. + fn extend_anti<'leap, Tuple: Ord, Func: Fn(&Tuple) -> Key>( + &'leap self, + key_func: Func, + ) -> extend_anti::ExtendAnti<'leap, Key, Val, Tuple, Func> + where + Key: 'leap, + Val: 'leap; + /// Extend with any value if tuple is present in relation. + fn filter_with<'leap, Tuple: Ord, Func: Fn(&Tuple) -> (Key, Val)>( + &'leap self, + key_func: Func, + ) -> filter_with::FilterWith<'leap, Key, Val, Tuple, Func> + where + Key: 'leap, + Val: 'leap; + /// Extend with any value if tuple is absent from relation. + fn filter_anti<'leap, Tuple: Ord, Func: Fn(&Tuple) -> (Key, Val)>( + &'leap self, + key_func: Func, + ) -> filter_anti::FilterAnti<'leap, Key, Val, Tuple, Func> + where + Key: 'leap, + Val: 'leap; +} + +impl<Key: Ord, Val: Ord> RelationLeaper<Key, Val> for Relation<(Key, Val)> { + fn extend_with<'leap, Tuple: Ord, Func: Fn(&Tuple) -> Key>( + &'leap self, + key_func: Func, + ) -> extend_with::ExtendWith<'leap, Key, Val, Tuple, Func> + where + Key: 'leap, + Val: 'leap, + { + extend_with::ExtendWith::from(self, key_func) + } + fn extend_anti<'leap, Tuple: Ord, Func: Fn(&Tuple) -> Key>( + &'leap self, + key_func: Func, + ) -> extend_anti::ExtendAnti<'leap, Key, Val, Tuple, Func> + where + Key: 'leap, + Val: 'leap, + { + extend_anti::ExtendAnti::from(self, key_func) + } + fn filter_with<'leap, Tuple: Ord, Func: Fn(&Tuple) -> (Key, Val)>( + &'leap self, + key_func: Func, + ) -> filter_with::FilterWith<'leap, Key, Val, Tuple, Func> + where + Key: 'leap, + Val: 'leap, + { + filter_with::FilterWith::from(self, key_func) + } + fn filter_anti<'leap, Tuple: Ord, Func: Fn(&Tuple) -> (Key, Val)>( + &'leap self, + key_func: Func, + ) -> filter_anti::FilterAnti<'leap, Key, Val, Tuple, Func> + where + Key: 'leap, + Val: 'leap, + { + filter_anti::FilterAnti::from(self, key_func) + } +} + +pub(crate) mod extend_with { + use super::{binary_search, Leaper, Leapers, Relation}; + use crate::join::gallop; + + /// Wraps a Relation<Tuple> as a leaper. + pub struct ExtendWith<'leap, Key, Val, Tuple, Func> + where + Key: Ord + 'leap, + Val: Ord + 'leap, + Tuple: Ord, + Func: Fn(&Tuple) -> Key, + { + relation: &'leap Relation<(Key, Val)>, + start: usize, + end: usize, + key_func: Func, + phantom: ::std::marker::PhantomData<Tuple>, + } + + impl<'leap, Key, Val, Tuple, Func> ExtendWith<'leap, Key, Val, Tuple, Func> + where + Key: Ord + 'leap, + Val: Ord + 'leap, + Tuple: Ord, + Func: Fn(&Tuple) -> Key, + { + /// Constructs a ExtendWith from a relation and key and value function. + pub fn from(relation: &'leap Relation<(Key, Val)>, key_func: Func) -> Self { + ExtendWith { + relation, + start: 0, + end: 0, + key_func, + phantom: ::std::marker::PhantomData, + } + } + } + + impl<'leap, Key, Val, Tuple, Func> Leaper<'leap, Tuple, Val> + for ExtendWith<'leap, Key, Val, Tuple, Func> + where + Key: Ord + 'leap, + Val: Ord + 'leap, + Tuple: Ord, + Func: Fn(&Tuple) -> Key, + { + fn count(&mut self, prefix: &Tuple) -> usize { + let key = (self.key_func)(prefix); + self.start = binary_search(&self.relation[..], |x| &x.0 < &key); + let slice1 = &self.relation[self.start..]; + let slice2 = gallop(slice1, |x| &x.0 <= &key); + self.end = self.relation.len() - slice2.len(); + slice1.len() - slice2.len() + } + fn propose(&mut self, _prefix: &Tuple, values: &mut Vec<&'leap Val>) { + let slice = &self.relation[self.start..self.end]; + values.extend(slice.iter().map(|&(_, ref val)| val)); + } + fn intersect(&mut self, _prefix: &Tuple, values: &mut Vec<&'leap Val>) { + let mut slice = &self.relation[self.start..self.end]; + values.retain(|v| { + slice = gallop(slice, |kv| &kv.1 < v); + slice.get(0).map(|kv| &kv.1) == Some(v) + }); + } + } + + impl<'leap, Key, Val, Tuple, Func> Leapers<'leap, Tuple, Val> + for ExtendWith<'leap, Key, Val, Tuple, Func> + where + Key: Ord + 'leap, + Val: Ord + 'leap, + Tuple: Ord, + Func: Fn(&Tuple) -> Key, + { + fn for_each_count(&mut self, tuple: &Tuple, mut op: impl FnMut(usize, usize)) { + op(0, self.count(tuple)) + } + + fn propose(&mut self, tuple: &Tuple, min_index: usize, values: &mut Vec<&'leap Val>) { + assert_eq!(min_index, 0); + Leaper::propose(self, tuple, values); + } + + fn intersect(&mut self, _: &Tuple, min_index: usize, _: &mut Vec<&'leap Val>) { + assert_eq!(min_index, 0); + } + } +} + +pub(crate) mod extend_anti { + use super::{binary_search, Leaper, Relation}; + use crate::join::gallop; + + /// Wraps a Relation<Tuple> as a leaper. + pub struct ExtendAnti<'leap, Key, Val, Tuple, Func> + where + Key: Ord + 'leap, + Val: Ord + 'leap, + Tuple: Ord, + Func: Fn(&Tuple) -> Key, + { + relation: &'leap Relation<(Key, Val)>, + key_func: Func, + phantom: ::std::marker::PhantomData<Tuple>, + } + + impl<'leap, Key, Val, Tuple, Func> ExtendAnti<'leap, Key, Val, Tuple, Func> + where + Key: Ord + 'leap, + Val: Ord + 'leap, + Tuple: Ord, + Func: Fn(&Tuple) -> Key, + { + /// Constructs a ExtendAnti from a relation and key and value function. + pub fn from(relation: &'leap Relation<(Key, Val)>, key_func: Func) -> Self { + ExtendAnti { + relation, + key_func, + phantom: ::std::marker::PhantomData, + } + } + } + + impl<'leap, Key: Ord, Val: Ord + 'leap, Tuple: Ord, Func> Leaper<'leap, Tuple, Val> + for ExtendAnti<'leap, Key, Val, Tuple, Func> + where + Key: Ord + 'leap, + Val: Ord + 'leap, + Tuple: Ord, + Func: Fn(&Tuple) -> Key, + { + fn count(&mut self, _prefix: &Tuple) -> usize { + usize::max_value() + } + fn propose(&mut self, _prefix: &Tuple, _values: &mut Vec<&'leap Val>) { + panic!("ExtendAnti::propose(): variable apparently unbound."); + } + fn intersect(&mut self, prefix: &Tuple, values: &mut Vec<&'leap Val>) { + let key = (self.key_func)(prefix); + let start = binary_search(&self.relation[..], |x| &x.0 < &key); + let slice1 = &self.relation[start..]; + let slice2 = gallop(slice1, |x| &x.0 <= &key); + let mut slice = &slice1[..(slice1.len() - slice2.len())]; + if !slice.is_empty() { + values.retain(|v| { + slice = gallop(slice, |kv| &kv.1 < v); + slice.get(0).map(|kv| &kv.1) != Some(v) + }); + } + } + } +} + +pub(crate) mod filter_with { + + use super::{Leaper, Leapers, Relation}; + + /// Wraps a Relation<Tuple> as a leaper. + pub struct FilterWith<'leap, Key, Val, Tuple, Func> + where + Key: Ord + 'leap, + Val: Ord + 'leap, + Tuple: Ord, + Func: Fn(&Tuple) -> (Key, Val), + { + relation: &'leap Relation<(Key, Val)>, + key_func: Func, + phantom: ::std::marker::PhantomData<Tuple>, + } + + impl<'leap, Key, Val, Tuple, Func> FilterWith<'leap, Key, Val, Tuple, Func> + where + Key: Ord + 'leap, + Val: Ord + 'leap, + Tuple: Ord, + Func: Fn(&Tuple) -> (Key, Val), + { + /// Constructs a FilterWith from a relation and key and value function. + pub fn from(relation: &'leap Relation<(Key, Val)>, key_func: Func) -> Self { + FilterWith { + relation, + key_func, + phantom: ::std::marker::PhantomData, + } + } + } + + impl<'leap, Key, Val, Val2, Tuple, Func> Leaper<'leap, Tuple, Val2> + for FilterWith<'leap, Key, Val, Tuple, Func> + where + Key: Ord + 'leap, + Val: Ord + 'leap, + Tuple: Ord, + Func: Fn(&Tuple) -> (Key, Val), + { + fn count(&mut self, prefix: &Tuple) -> usize { + let key_val = (self.key_func)(prefix); + if self.relation.binary_search(&key_val).is_ok() { + usize::max_value() + } else { + 0 + } + } + fn propose(&mut self, _prefix: &Tuple, _values: &mut Vec<&'leap Val2>) { + panic!("FilterWith::propose(): variable apparently unbound."); + } + fn intersect(&mut self, _prefix: &Tuple, _values: &mut Vec<&'leap Val2>) { + // Only here because we didn't return zero above, right? + } + } + + impl<'leap, Key, Val, Tuple, Func> Leapers<'leap, Tuple, ()> + for FilterWith<'leap, Key, Val, Tuple, Func> + where + Key: Ord + 'leap, + Val: Ord + 'leap, + Tuple: Ord, + Func: Fn(&Tuple) -> (Key, Val), + { + fn for_each_count(&mut self, tuple: &Tuple, mut op: impl FnMut(usize, usize)) { + if <Self as Leaper<Tuple, ()>>::count(self, tuple) == 0 { + op(0, 0) + } else { + op(0, 1) + } + } + + fn propose(&mut self, _: &Tuple, min_index: usize, values: &mut Vec<&'leap ()>) { + assert_eq!(min_index, 0); + values.push(&()); + } + + fn intersect(&mut self, _: &Tuple, min_index: usize, values: &mut Vec<&'leap ()>) { + assert_eq!(min_index, 0); + assert_eq!(values.len(), 1); + } + } +} + +pub(crate) mod filter_anti { + + use super::{Leaper, Leapers, Relation}; + + /// Wraps a Relation<Tuple> as a leaper. + pub struct FilterAnti<'leap, Key, Val, Tuple, Func> + where + Key: Ord + 'leap, + Val: Ord + 'leap, + Tuple: Ord, + Func: Fn(&Tuple) -> (Key, Val), + { + relation: &'leap Relation<(Key, Val)>, + key_func: Func, + phantom: ::std::marker::PhantomData<Tuple>, + } + + impl<'leap, Key, Val, Tuple, Func> FilterAnti<'leap, Key, Val, Tuple, Func> + where + Key: Ord + 'leap, + Val: Ord + 'leap, + Tuple: Ord, + Func: Fn(&Tuple) -> (Key, Val), + { + /// Constructs a FilterAnti from a relation and key and value function. + pub fn from(relation: &'leap Relation<(Key, Val)>, key_func: Func) -> Self { + FilterAnti { + relation, + key_func, + phantom: ::std::marker::PhantomData, + } + } + } + + impl<'leap, Key: Ord, Val: Ord + 'leap, Val2, Tuple: Ord, Func> Leaper<'leap, Tuple, Val2> + for FilterAnti<'leap, Key, Val, Tuple, Func> + where + Key: Ord + 'leap, + Val: Ord + 'leap, + Tuple: Ord, + Func: Fn(&Tuple) -> (Key, Val), + { + fn count(&mut self, prefix: &Tuple) -> usize { + let key_val = (self.key_func)(prefix); + if self.relation.binary_search(&key_val).is_ok() { + 0 + } else { + usize::max_value() + } + } + fn propose(&mut self, _prefix: &Tuple, _values: &mut Vec<&'leap Val2>) { + panic!("FilterAnti::propose(): variable apparently unbound."); + } + fn intersect(&mut self, _prefix: &Tuple, _values: &mut Vec<&'leap Val2>) { + // Only here because we didn't return zero above, right? + } + } + + impl<'leap, Key, Val, Tuple, Func> Leapers<'leap, Tuple, ()> + for FilterAnti<'leap, Key, Val, Tuple, Func> + where + Key: Ord + 'leap, + Val: Ord + 'leap, + Tuple: Ord, + Func: Fn(&Tuple) -> (Key, Val), + { + fn for_each_count(&mut self, tuple: &Tuple, mut op: impl FnMut(usize, usize)) { + if <Self as Leaper<Tuple, ()>>::count(self, tuple) == 0 { + op(0, 0) + } else { + op(0, 1) + } + } + + fn propose(&mut self, _: &Tuple, min_index: usize, values: &mut Vec<&'leap ()>) { + // We only get here if `tuple` is *not* a member of `self.relation` + assert_eq!(min_index, 0); + values.push(&()); + } + + fn intersect(&mut self, _: &Tuple, min_index: usize, values: &mut Vec<&'leap ()>) { + // We only get here if `tuple` is not a member of `self.relation` + assert_eq!(min_index, 0); + assert_eq!(values.len(), 1); + } + } +} + +fn binary_search<T>(slice: &[T], mut cmp: impl FnMut(&T) -> bool) -> usize { + // we maintain the invariant that `lo` many elements of `slice` satisfy `cmp`. + // `hi` is maintained at the first element we know does not satisfy `cmp`. + + let mut hi = slice.len(); + let mut lo = 0; + while lo < hi { + let mid = lo + (hi - lo) / 2; + if cmp(&slice[mid]) { + lo = mid + 1; + } else { + hi = mid; + } + } + lo +} diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/log/.cargo-checksum.json b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/log/.cargo-checksum.json new file mode 100644 index 0000000..42ea021 --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/log/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"CHANGELOG.md":"df7d7ea4256611dd5e3bf160e39bb3f8b665c6805ae47fdbf28acf9f77245ffd","Cargo.toml":"2161251dd0dfbea680a9d5fd762973e343fc5215794681c5ffd641faab9a4e4c","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb","README.md":"a23bbe55ac94081711c081a63df10d324a8a26f4b836952cb3c45c9318a03152","benches/value.rs":"b613ff353d3cf0ef8cb98e4ca461ea929b8ba553fe299f2eb2942d77a5b1b6a0","src/__private_api.rs":"da677f1e29e3cb135c971247031bc0eb20324294ab5c1c74c5118f87e45518ae","src/kv/error.rs":"6dae12424164c33b93915f5e70bd6d99d616c969c8bfb543806721dd9b423981","src/kv/key.rs":"9439e91c3ab3f9574a6a11a0347c7b63fdf1652384a6b28411136e4373de2970","src/kv/mod.rs":"3521a5bcfd7f92dcfac6c3c948020d686fee696596c566333a27edbbcc8a4ea8","src/kv/source.rs":"73fbc180c824072d86f1f41f8c59c014db1d8988a86be38a9128d67d6aab06a5","src/kv/value.rs":"0aade52b8e3523a17d6114f8b664793862032a94ea1ee2a4f12a20dd729b92d4","src/lib.rs":"55c32130cd8b99cde2ea962a403cdade52d20e80088357ba2784ee53b2eb9a2c","src/macros.rs":"dfb98017d5f205fec632069ab857a18661d6d563cf5162eeef64d367cc3ad7f5","src/serde.rs":"35f520f62fdba0216ccee33e5b66ad8f81dee3af5b65b824f1816180c9350df5","triagebot.toml":"a135e10c777cd13459559bdf74fb704c1379af7c9b0f70bc49fa6f5a837daa81"},"package":"a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"}
\ No newline at end of file diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/log/CHANGELOG.md b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/log/CHANGELOG.md new file mode 100644 index 0000000..2c89834 --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/log/CHANGELOG.md @@ -0,0 +1,324 @@ +# Change Log + +## [Unreleased] + +## [0.4.22] - 2024-06-27 + +## What's Changed +* Add some clarifications to the library docs by @KodrAus in https://github.com/rust-lang/log/pull/620 +* Add links to `colog` crate by @chrivers in https://github.com/rust-lang/log/pull/621 +* adding line_number test + updating some testing infrastructure by @DIvkov575 in https://github.com/rust-lang/log/pull/619 +* Clarify the actual set of functions that can race in _racy variants by @KodrAus in https://github.com/rust-lang/log/pull/623 +* Replace deprecated std::sync::atomic::spin_loop_hint() by @Catamantaloedis in https://github.com/rust-lang/log/pull/625 +* Check usage of max_level features by @Thomasdezeeuw in https://github.com/rust-lang/log/pull/627 +* Remove unneeded import by @Thomasdezeeuw in https://github.com/rust-lang/log/pull/628 +* Loosen orderings for logger initialization in https://github.com/rust-lang/log/pull/632. Originally by @pwoolcoc in https://github.com/rust-lang/log/pull/599 +* Use Location::caller() for file and line info in https://github.com/rust-lang/log/pull/633. Originally by @Cassy343 in https://github.com/rust-lang/log/pull/520 + +## New Contributors +* @chrivers made their first contribution in https://github.com/rust-lang/log/pull/621 +* @DIvkov575 made their first contribution in https://github.com/rust-lang/log/pull/619 +* @Catamantaloedis made their first contribution in https://github.com/rust-lang/log/pull/625 + +**Full Changelog**: https://github.com/rust-lang/log/compare/0.4.21...0.4.22 + +## [0.4.21] - 2024-02-27 + +## What's Changed +* Minor clippy nits by @nyurik in https://github.com/rust-lang/log/pull/578 +* Simplify Display impl by @nyurik in https://github.com/rust-lang/log/pull/579 +* Set all crates to 2021 edition by @nyurik in https://github.com/rust-lang/log/pull/580 +* Various changes based on review by @Thomasdezeeuw in https://github.com/rust-lang/log/pull/583 +* Fix typo in file_static() method doc by @dimo414 in https://github.com/rust-lang/log/pull/590 +* Specialize empty key value pairs by @EFanZh in https://github.com/rust-lang/log/pull/576 +* Fix incorrect lifetime in Value::to_str() by @peterjoel in https://github.com/rust-lang/log/pull/587 +* Remove some API of the key-value feature by @Thomasdezeeuw in https://github.com/rust-lang/log/pull/585 +* Add logcontrol-log and log-reload by @swsnr in https://github.com/rust-lang/log/pull/595 +* Add Serialization section to kv::Value docs by @Thomasdezeeuw in https://github.com/rust-lang/log/pull/593 +* Rename Value::to_str to to_cow_str by @Thomasdezeeuw in https://github.com/rust-lang/log/pull/592 +* Clarify documentation and simplify initialization of `STATIC_MAX_LEVEL` by @ptosi in https://github.com/rust-lang/log/pull/594 +* Update docs to 2021 edition, test by @nyurik in https://github.com/rust-lang/log/pull/577 +* Add "alterable_logger" link to README.md by @brummer-simon in https://github.com/rust-lang/log/pull/589 +* Normalize line ending by @EFanZh in https://github.com/rust-lang/log/pull/602 +* Remove `ok_or` in favor of `Option::ok_or` by @AngelicosPhosphoros in https://github.com/rust-lang/log/pull/607 +* Use `Acquire` ordering for initialization check by @AngelicosPhosphoros in https://github.com/rust-lang/log/pull/610 +* Get structured logging API ready for stabilization by @KodrAus in https://github.com/rust-lang/log/pull/613 + +## New Contributors +* @nyurik made their first contribution in https://github.com/rust-lang/log/pull/578 +* @dimo414 made their first contribution in https://github.com/rust-lang/log/pull/590 +* @peterjoel made their first contribution in https://github.com/rust-lang/log/pull/587 +* @ptosi made their first contribution in https://github.com/rust-lang/log/pull/594 +* @brummer-simon made their first contribution in https://github.com/rust-lang/log/pull/589 +* @AngelicosPhosphoros made their first contribution in https://github.com/rust-lang/log/pull/607 + +## [0.4.20] - 2023-07-11 + +* Remove rustversion dev-dependency by @Thomasdezeeuw in https://github.com/rust-lang/log/pull/568 +* Remove `local_inner_macros` usage by @EFanZh in https://github.com/rust-lang/log/pull/570 + +## [0.4.19] - 2023-06-10 + +* Use target_has_atomic instead of the old atomic_cas cfg by @GuillaumeGomez in https://github.com/rust-lang/log/pull/555 +* Put MSRV into Cargo.toml by @est31 in https://github.com/rust-lang/log/pull/557 + +## [0.4.18] - 2023-05-28 + +* fix markdown links (again) by @hellow554 in https://github.com/rust-lang/log/pull/513 +* add cargo doc to workflow by @hellow554 in https://github.com/rust-lang/log/pull/515 +* Apply Clippy lints by @hellow554 in https://github.com/rust-lang/log/pull/516 +* Replace ad-hoc eq_ignore_ascii_case with slice::eq_ignore_ascii_case by @glandium in https://github.com/rust-lang/log/pull/519 +* fix up windows targets by @KodrAus in https://github.com/rust-lang/log/pull/528 +* typo fix by @jiangying000 in https://github.com/rust-lang/log/pull/529 +* Remove dependency on cfg_if by @EriKWDev in https://github.com/rust-lang/log/pull/536 +* GitHub Workflows security hardening by @sashashura in https://github.com/rust-lang/log/pull/538 +* Fix build status badge by @atouchet in https://github.com/rust-lang/log/pull/539 +* Add call_logger to the documentation by @a1ecbr0wn in https://github.com/rust-lang/log/pull/547 +* Use stable internals for key-value API by @KodrAus in https://github.com/rust-lang/log/pull/550 +* Change wording of list of implementations by @Thomasdezeeuw in https://github.com/rust-lang/log/pull/553 +* Add std-logger to list of implementations by @Thomasdezeeuw in https://github.com/rust-lang/log/pull/554 +* Add `set_max_level_racy` and gate `set_max_level` by @djkoloski in https://github.com/rust-lang/log/pull/544 +* [doc] src/lib.rs : prefix an unused variable with an underscore by @OccupyMars2025 in https://github.com/rust-lang/log/pull/561 +* [doc] src/macros.rs : correct grammar errors of an example in lib documentation by @OccupyMars2025 in https://github.com/rust-lang/log/pull/562 + +## [0.4.17] - 2022-04-29 + +* Update `kv_unstable` internal dependencies. + +## [0.4.16] - 2022-03-22 + +* Fix a conflict with unqualified `Option` use in macros. + +## [0.4.15] - 2022-02-23 + +* Silence a warning about the deprecated `spin_loop_hint`. +* Relax ordering in the atomic `set_max_level` call. +* Add thumbv4t-none-eabi to targets that don't support atomics +* Allow levels to be iterated over. +* Implement `Log` on some common wrapper types. +* Improvements to test coverage. +* Improvements to documentation. +* Add key-value support to the `log!` macros. +* Tighten `kv_unstable` internal dependencies so they don't bump past their current alpha. +* Add a simple visit API to `kv_unstable`. +* Support `NonZero*` integers as values in structured logging +* Support static strings as keys in structured logging + +## [0.4.14] - 2021-01-27 + +* Remove the `__private_api_log_lit` special case. +* Fixed incorrect combination of `kv_unstable` and `std` features causing compile failures. +* Remove unstable `Value::to_*` conversions that were incorrectly using `as`. +* Rename unstable `Value::to_error` to `Value::to_borrowed_error`. + +## [0.4.13] - 2021-01-11 + +* This is the same as `0.4.11`, except with a `kv_unstable_std` feature added to aid migrating current dependents to `0.4.14` (which was originally going to be `0.4.13` until it was decided to create a patch from `0.4.11` to minimize disruption). + +## [0.4.12] - 2020-12-24 + +### New + +* Support platforms without atomics by racing instead of failing to compile +* Implement `Log` for `Box<T: Log>` +* Update `cfg-if` to `1.0` +* Internal reworks of the structured logging API. Removed the `Fill` API +and added `source::as_map` and `source::as_list` to easily serialize a `Source` +as either a map of `{key: value, ..}` or as a list of `[(key, value), ..]`. + +### Fixed + +* Fixed deserialization of `LevelFilter` to use their `u64` index variants + +## [0.4.11] - 2020-07-09 + +### New + +* Support coercing structured values into concrete types. +* Reference the `win_dbg_logger` in the readme. + +### Fixed + +* Updates a few deprecated items used internally. +* Fixed issues in docs and expands sections. +* Show the correct build badge in the readme. +* Fix up a possible inference breakage with structured value errors. +* Respect formatting flags in structured value formatting. + +## [0.4.10] - 2019-12-16 (yanked) + +### Fixed + +* Fixed the `log!` macros so they work in expression context (this regressed in `0.4.9`, which has been yanked). + +## [0.4.9] - 2019-12-12 (yanked) + +### Minimum Supported Rust Version + +This release bumps the minimum compiler version to `1.31.0`. This was mainly needed for `cfg-if`, +but between `1.16.0` and `1.31.0` there are a lot of language and library improvements we now +take advantage of. + +### New + +* Unstable support for capturing key-value pairs in a record using the `log!` macros + +### Improved + +* Better documentation for max level filters. +* Internal updates to line up with bumped MSRV + +## [0.4.8] - 2019-07-28 + +### New + +* Support attempting to get `Record` fields as static strings. + +## [0.4.7] - 2019-07-06 + +### New + +* Support for embedded environments with thread-unsafe initialization. +* Initial unstable support for capturing structured data under the `kv_unstable` +feature gate. This new API doesn't affect existing users and may change in future +patches (so those changes may not appear in the changelog until it stabilizes). + +### Improved + +* Docs for using `log` with the 2018 edition. +* Error messages for macros missing arguments. + +## [0.4.6] - 2018-10-27 + +### Improved + +* Support 2018-style macro import for the `log_enabled!` macro. + +## [0.4.5] - 2018-09-03 + +### Improved + +* Make `log`'s internal helper macros less likely to conflict with user-defined + macros. + +## [0.4.4] - 2018-08-17 + +### Improved + +* Support 2018-style imports of the log macros. + +## [0.4.3] - 2018-06-29 + +### Improved + +* More code generation improvements. + +## [0.4.2] - 2018-06-05 + +### Improved + +* Log invocations now generate less code. + +### Fixed + +* Example Logger implementations now properly set the max log level. + +## [0.4.1] - 2017-12-30 + +### Fixed + +* Some doc links were fixed. + +## [0.4.0] - 2017-12-24 + +The changes in this release include cleanup of some obscure functionality and a more robust public +API designed to support bridges to other logging systems, and provide more flexibility to new +features in the future. + +### Compatibility + +Vast portions of the Rust ecosystem use the 0.3.x release series of log, and we don't want to force +the community to go through the pain of upgrading every crate to 0.4.x at the exact same time. Along +with 0.4.0, we've published a new 0.3.9 release which acts as a "shim" over 0.4.0. This will allow +crates using either version to coexist without losing messages from one side or the other. + +There is one caveat - a log message generated by a crate using 0.4.x but consumed by a logging +implementation using 0.3.x will not have a file name or module path. Applications affected by this +can upgrade their logging implementations to one using 0.4.x to avoid losing this information. The +other direction does not lose any information, fortunately! + +**TL;DR** Libraries should feel comfortable upgrading to 0.4.0 without treating that as a breaking +change. Applications may need to update their logging implementation (e.g. env-logger) to a newer +version using log 0.4.x to avoid losing module and file information. + +### New + +* The crate is now `no_std` by default. +* `Level` and `LevelFilter` now implement `Serialize` and `Deserialize` when the `serde` feature is + enabled. +* The `Record` and `Metadata` types can now be constructed by third-party code via a builder API. +* The `logger` free function returns a reference to the logger implementation. This, along with the + ability to construct `Record`s, makes it possible to bridge from another logging framework to + this one without digging into the private internals of the crate. The standard `error!` `warn!`, + etc, macros now exclusively use the public API of the crate rather than "secret" internal APIs. +* `Log::flush` has been added to allow crates to tell the logging implementation to ensure that all + "in flight" log events have been persisted. This can be used, for example, just before an + application exits to ensure that asynchronous log sinks finish their work. + +### Removed + +* The `shutdown` and `shutdown_raw` functions have been removed. Supporting shutdown significantly + complicated the implementation and imposed a performance cost on each logging operation. +* The `log_panics` function and its associated `nightly` Cargo feature have been removed. Use the + [log-panics](https://crates.io/crates/log-panics) instead. + +### Changed + +* The `Log` prefix has been removed from type names. For example, `LogLevelFilter` is now + `LevelFilter`, and `LogRecord` is now `Record`. +* The `MaxLogLevelFilter` object has been removed in favor of a `set_max_level` free function. +* The `set_logger` free functions have been restructured. The logger is now directly passed to the + functions rather than a closure which returns the logger. `set_logger` now takes a `&'static + Log` and is usable in `no_std` contexts in place of the old `set_logger_raw`. `set_boxed_logger` + is a convenience function which takes a `Box<Log>` but otherwise acts like `set_logger`. It + requires the `std` feature. +* The `file` and `module_path` values in `Record` no longer have the `'static` lifetime to support + integration with other logging frameworks that don't provide a `'static` lifetime for the + equivalent values. +* The `file`, `line`, and `module_path` values in `Record` are now `Option`s to support integration + with other logging frameworks that don't provide those values. + +### In the Future + +* We're looking to add support for *structured* logging - the inclusion of extra key-value pairs of + information in a log event in addition to the normal string message. This should be able to be + added in a backwards compatible manner to the 0.4.x series when the design is worked out. + +## Older + +Look at the [release tags] for information about older releases. + +[Unreleased]: https://github.com/rust-lang-nursery/log/compare/0.4.21...HEAD +[0.4.21]: https://github.com/rust-lang/log/compare/0.4.20...0.4.21 +[0.4.20]: https://github.com/rust-lang-nursery/log/compare/0.4.19...0.4.20 +[0.4.19]: https://github.com/rust-lang-nursery/log/compare/0.4.18...0.4.19 +[0.4.18]: https://github.com/rust-lang-nursery/log/compare/0.4.17...0.4.18 +[0.4.17]: https://github.com/rust-lang-nursery/log/compare/0.4.16...0.4.17 +[0.4.16]: https://github.com/rust-lang-nursery/log/compare/0.4.15...0.4.16 +[0.4.15]: https://github.com/rust-lang-nursery/log/compare/0.4.13...0.4.15 +[0.4.14]: https://github.com/rust-lang-nursery/log/compare/0.4.13...0.4.14 +[0.4.13]: https://github.com/rust-lang-nursery/log/compare/0.4.11...0.4.13 +[0.4.12]: https://github.com/rust-lang-nursery/log/compare/0.4.11...0.4.12 +[0.4.11]: https://github.com/rust-lang-nursery/log/compare/0.4.10...0.4.11 +[0.4.10]: https://github.com/rust-lang-nursery/log/compare/0.4.9...0.4.10 +[0.4.9]: https://github.com/rust-lang-nursery/log/compare/0.4.8...0.4.9 +[0.4.8]: https://github.com/rust-lang-nursery/log/compare/0.4.7...0.4.8 +[0.4.7]: https://github.com/rust-lang-nursery/log/compare/0.4.6...0.4.7 +[0.4.6]: https://github.com/rust-lang-nursery/log/compare/0.4.5...0.4.6 +[0.4.5]: https://github.com/rust-lang-nursery/log/compare/0.4.4...0.4.5 +[0.4.4]: https://github.com/rust-lang-nursery/log/compare/0.4.3...0.4.4 +[0.4.3]: https://github.com/rust-lang-nursery/log/compare/0.4.2...0.4.3 +[0.4.2]: https://github.com/rust-lang-nursery/log/compare/0.4.1...0.4.2 +[0.4.1]: https://github.com/rust-lang-nursery/log/compare/0.4.0...0.4.1 +[0.4.0]: https://github.com/rust-lang-nursery/log/compare/0.3.8...0.4.0 +[release tags]: https://github.com/rust-lang-nursery/log/releases diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/log/Cargo.toml b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/log/Cargo.toml new file mode 100644 index 0000000..313a005 --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/log/Cargo.toml @@ -0,0 +1,139 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +rust-version = "1.60.0" +name = "log" +version = "0.4.22" +authors = ["The Rust Project Developers"] +exclude = ["rfcs/**/*"] +description = """ +A lightweight logging facade for Rust +""" +documentation = "https://docs.rs/log" +readme = "README.md" +keywords = ["logging"] +categories = ["development-tools::debugging"] +license = "MIT OR Apache-2.0" +repository = "https://github.com/rust-lang/log" + +[package.metadata.docs.rs] +features = [ + "std", + "serde", + "kv_std", + "kv_sval", + "kv_serde", +] + +[[test]] +name = "integration" +path = "tests/integration.rs" +harness = false + +[[test]] +name = "macros" +path = "tests/macros.rs" +harness = true + +[dependencies.serde] +version = "1.0" +optional = true +default-features = false + +[dependencies.sval] +version = "2.1" +optional = true +default-features = false + +[dependencies.sval_ref] +version = "2.1" +optional = true +default-features = false + +[dependencies.value-bag] +version = "1.7" +features = ["inline-i128"] +optional = true +default-features = false + +[dev-dependencies.proc-macro2] +version = "1.0.63" +default-features = false + +[dev-dependencies.serde] +version = "1.0" +features = ["derive"] + +[dev-dependencies.serde_json] +version = "1.0" + +[dev-dependencies.serde_test] +version = "1.0" + +[dev-dependencies.sval] +version = "2.1" + +[dev-dependencies.sval_derive] +version = "2.1" + +[dev-dependencies.value-bag] +version = "1.7" +features = ["test"] + +[features] +kv = [] +kv_serde = [ + "kv_std", + "value-bag/serde", + "serde", +] +kv_std = [ + "std", + "kv", + "value-bag/error", +] +kv_sval = [ + "kv", + "value-bag/sval", + "sval", + "sval_ref", +] +kv_unstable = [ + "kv", + "value-bag", +] +kv_unstable_serde = [ + "kv_serde", + "kv_unstable_std", +] +kv_unstable_std = [ + "kv_std", + "kv_unstable", +] +kv_unstable_sval = [ + "kv_sval", + "kv_unstable", +] +max_level_debug = [] +max_level_error = [] +max_level_info = [] +max_level_off = [] +max_level_trace = [] +max_level_warn = [] +release_max_level_debug = [] +release_max_level_error = [] +release_max_level_info = [] +release_max_level_off = [] +release_max_level_trace = [] +release_max_level_warn = [] +std = [] diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/log/LICENSE-APACHE b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/log/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/log/LICENSE-APACHE @@ -0,0 +1,201 @@ + 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. diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/log/LICENSE-MIT b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/log/LICENSE-MIT new file mode 100644 index 0000000..39d4bdb --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/log/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2014 The Rust Project Developers + +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. diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/log/README.md b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/log/README.md new file mode 100644 index 0000000..d4a08b1 --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/log/README.md @@ -0,0 +1,130 @@ +log +=== + +A Rust library providing a lightweight logging *facade*. + +[](https://github.com/rust-lang/log/actions) +[](https://crates.io/crates/log) +[](https://docs.rs/log) + + +* [`log` documentation](https://docs.rs/log) + +A logging facade provides a single logging API that abstracts over the actual +logging implementation. Libraries can use the logging API provided by this +crate, and the consumer of those libraries can choose the logging +implementation that is most suitable for its use case. + + +## Minimum supported `rustc` + +`1.60.0+` + +This version is explicitly tested in CI and may be bumped in any release as needed. Maintaining compatibility with older compilers is a priority though, so the bar for bumping the minimum supported version is set very high. Any changes to the supported minimum version will be called out in the release notes. + +## Usage + +### In libraries + +Libraries should link only to the `log` crate, and use the provided macros to +log whatever information will be useful to downstream consumers: + +```toml +[dependencies] +log = "0.4" +``` + +```rust +use log::{info, trace, warn}; + +pub fn shave_the_yak(yak: &mut Yak) { + trace!("Commencing yak shaving"); + + loop { + match find_a_razor() { + Ok(razor) => { + info!("Razor located: {razor}"); + yak.shave(razor); + break; + } + Err(err) => { + warn!("Unable to locate a razor: {err}, retrying"); + } + } + } +} +``` + +### In executables + +In order to produce log output, executables have to use a logger implementation compatible with the facade. +There are many available implementations to choose from, here are some options: + +* Simple minimal loggers: + * [`env_logger`](https://docs.rs/env_logger/*/env_logger/) + * [`colog`](https://docs.rs/colog/*/colog/) + * [`simple_logger`](https://docs.rs/simple_logger/*/simple_logger/) + * [`simplelog`](https://docs.rs/simplelog/*/simplelog/) + * [`pretty_env_logger`](https://docs.rs/pretty_env_logger/*/pretty_env_logger/) + * [`stderrlog`](https://docs.rs/stderrlog/*/stderrlog/) + * [`flexi_logger`](https://docs.rs/flexi_logger/*/flexi_logger/) + * [`call_logger`](https://docs.rs/call_logger/*/call_logger/) + * [`std-logger`](https://docs.rs/std-logger/*/std_logger/) + * [`structured-logger`](https://docs.rs/structured-logger/latest/structured_logger/) +* Complex configurable frameworks: + * [`log4rs`](https://docs.rs/log4rs/*/log4rs/) + * [`fern`](https://docs.rs/fern/*/fern/) +* Adaptors for other facilities: + * [`syslog`](https://docs.rs/syslog/*/syslog/) + * [`systemd-journal-logger`](https://docs.rs/systemd-journal-logger/*/systemd_journal_logger/) + * [`slog-stdlog`](https://docs.rs/slog-stdlog/*/slog_stdlog/) + * [`android_log`](https://docs.rs/android_log/*/android_log/) + * [`win_dbg_logger`](https://docs.rs/win_dbg_logger/*/win_dbg_logger/) + * [`db_logger`](https://docs.rs/db_logger/*/db_logger/) + * [`log-to-defmt`](https://docs.rs/log-to-defmt/*/log_to_defmt/) + * [`logcontrol-log`](https://docs.rs/logcontrol-log/*/logcontrol_log/) +* For WebAssembly binaries: + * [`console_log`](https://docs.rs/console_log/*/console_log/) +* For dynamic libraries: + * You may need to construct [an FFI-safe wrapper over `log`](https://github.com/rust-lang/log/issues/421) to initialize in your libraries. +* Utilities: + * [`log_err`](https://docs.rs/log_err/*/log_err/) + * [`log-reload`](https://docs.rs/log-reload/*/log_reload/) + * [`alterable_logger`](https://docs.rs/alterable_logger/*/alterable_logger) + +Executables should choose a logger implementation and initialize it early in the +runtime of the program. Logger implementations will typically include a +function to do this. Any log messages generated before the logger is +initialized will be ignored. + +The executable itself may use the `log` crate to log as well. + +## Structured logging + +If you enable the `kv` feature, you can associate structured data with your log records: + +```rust +use log::{info, trace, warn}; + +pub fn shave_the_yak(yak: &mut Yak) { + // `yak:serde` will capture `yak` using its `serde::Serialize` impl + // + // You could also use `:?` for `Debug`, or `:%` for `Display`. For a + // full list, see the `log` crate documentation + trace!(target = "yak_events", yak:serde; "Commencing yak shaving"); + + loop { + match find_a_razor() { + Ok(razor) => { + info!(razor; "Razor located"); + yak.shave(razor); + break; + } + Err(e) => { + // `e:err` will capture `e` using its `std::error::Error` impl + warn!(e:err; "Unable to locate a razor, retrying"); + } + } + } +} +``` diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/log/benches/value.rs b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/log/benches/value.rs new file mode 100644 index 0000000..3d0f18b --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/log/benches/value.rs @@ -0,0 +1,27 @@ +#![cfg(feature = "kv")] +#![feature(test)] + +use log::kv::Value; + +#[bench] +fn u8_to_value(b: &mut test::Bencher) { + b.iter(|| Value::from(1u8)); +} + +#[bench] +fn u8_to_value_debug(b: &mut test::Bencher) { + b.iter(|| Value::from_debug(&1u8)); +} + +#[bench] +fn str_to_value_debug(b: &mut test::Bencher) { + b.iter(|| Value::from_debug(&"a string")); +} + +#[bench] +fn custom_to_value_debug(b: &mut test::Bencher) { + #[derive(Debug)] + struct A; + + b.iter(|| Value::from_debug(&A)); +} diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/log/src/__private_api.rs b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/log/src/__private_api.rs new file mode 100644 index 0000000..11bc2fc --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/log/src/__private_api.rs @@ -0,0 +1,123 @@ +//! WARNING: this is not part of the crate's public API and is subject to change at any time + +use self::sealed::KVs; +use crate::{Level, Metadata, Record}; +use std::fmt::Arguments; +use std::panic::Location; +pub use std::{format_args, module_path, stringify}; + +#[cfg(not(feature = "kv"))] +pub type Value<'a> = &'a str; + +mod sealed { + /// Types for the `kv` argument. + pub trait KVs<'a> { + fn into_kvs(self) -> Option<&'a [(&'a str, super::Value<'a>)]>; + } +} + +// Types for the `kv` argument. + +impl<'a> KVs<'a> for &'a [(&'a str, Value<'a>)] { + #[inline] + fn into_kvs(self) -> Option<&'a [(&'a str, Value<'a>)]> { + Some(self) + } +} + +impl<'a> KVs<'a> for () { + #[inline] + fn into_kvs(self) -> Option<&'a [(&'a str, Value<'a>)]> { + None + } +} + +// Log implementation. + +fn log_impl( + args: Arguments, + level: Level, + &(target, module_path, loc): &(&str, &'static str, &'static Location), + kvs: Option<&[(&str, Value)]>, +) { + #[cfg(not(feature = "kv"))] + if kvs.is_some() { + panic!("key-value support is experimental and must be enabled using the `kv` feature") + } + + let mut builder = Record::builder(); + + builder + .args(args) + .level(level) + .target(target) + .module_path_static(Some(module_path)) + .file_static(Some(loc.file())) + .line(Some(loc.line())); + + #[cfg(feature = "kv")] + builder.key_values(&kvs); + + crate::logger().log(&builder.build()); +} + +pub fn log<'a, K>( + args: Arguments, + level: Level, + target_module_path_and_loc: &(&str, &'static str, &'static Location), + kvs: K, +) where + K: KVs<'a>, +{ + log_impl(args, level, target_module_path_and_loc, kvs.into_kvs()) +} + +pub fn enabled(level: Level, target: &str) -> bool { + crate::logger().enabled(&Metadata::builder().level(level).target(target).build()) +} + +#[track_caller] +pub fn loc() -> &'static Location<'static> { + Location::caller() +} + +#[cfg(feature = "kv")] +mod kv_support { + use crate::kv; + + pub type Value<'a> = kv::Value<'a>; + + // NOTE: Many functions here accept a double reference &&V + // This is so V itself can be ?Sized, while still letting us + // erase it to some dyn Trait (because &T is sized) + + pub fn capture_to_value<'a, V: kv::ToValue + ?Sized>(v: &'a &'a V) -> Value<'a> { + v.to_value() + } + + pub fn capture_debug<'a, V: core::fmt::Debug + ?Sized>(v: &'a &'a V) -> Value<'a> { + Value::from_debug(v) + } + + pub fn capture_display<'a, V: core::fmt::Display + ?Sized>(v: &'a &'a V) -> Value<'a> { + Value::from_display(v) + } + + #[cfg(feature = "kv_std")] + pub fn capture_error<'a>(v: &'a (dyn std::error::Error + 'static)) -> Value<'a> { + Value::from_dyn_error(v) + } + + #[cfg(feature = "kv_sval")] + pub fn capture_sval<'a, V: sval::Value + ?Sized>(v: &'a &'a V) -> Value<'a> { + Value::from_sval(v) + } + + #[cfg(feature = "kv_serde")] + pub fn capture_serde<'a, V: serde::Serialize + ?Sized>(v: &'a &'a V) -> Value<'a> { + Value::from_serde(v) + } +} + +#[cfg(feature = "kv")] +pub use self::kv_support::*; diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/log/src/kv/error.rs b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/log/src/kv/error.rs new file mode 100644 index 0000000..7efa5af --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/log/src/kv/error.rs @@ -0,0 +1,94 @@ +use std::fmt; + +/// An error encountered while working with structured data. +#[derive(Debug)] +pub struct Error { + inner: Inner, +} + +#[derive(Debug)] +enum Inner { + #[cfg(feature = "std")] + Boxed(std_support::BoxedError), + Msg(&'static str), + #[cfg(feature = "value-bag")] + Value(crate::kv::value::inner::Error), + Fmt, +} + +impl Error { + /// Create an error from a message. + pub fn msg(msg: &'static str) -> Self { + Error { + inner: Inner::Msg(msg), + } + } + + // Not public so we don't leak the `crate::kv::value::inner` API + #[cfg(feature = "value-bag")] + pub(super) fn from_value(err: crate::kv::value::inner::Error) -> Self { + Error { + inner: Inner::Value(err), + } + } + + // Not public so we don't leak the `crate::kv::value::inner` API + #[cfg(feature = "value-bag")] + pub(super) fn into_value(self) -> crate::kv::value::inner::Error { + match self.inner { + Inner::Value(err) => err, + #[cfg(feature = "kv_std")] + _ => crate::kv::value::inner::Error::boxed(self), + #[cfg(not(feature = "kv_std"))] + _ => crate::kv::value::inner::Error::msg("error inspecting a value"), + } + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use self::Inner::*; + match &self.inner { + #[cfg(feature = "std")] + Boxed(err) => err.fmt(f), + #[cfg(feature = "value-bag")] + Value(err) => err.fmt(f), + Msg(msg) => msg.fmt(f), + Fmt => fmt::Error.fmt(f), + } + } +} + +impl From<fmt::Error> for Error { + fn from(_: fmt::Error) -> Self { + Error { inner: Inner::Fmt } + } +} + +#[cfg(feature = "std")] +mod std_support { + use super::*; + use std::{error, io}; + + pub(super) type BoxedError = Box<dyn error::Error + Send + Sync>; + + impl Error { + /// Create an error from a standard error type. + pub fn boxed<E>(err: E) -> Self + where + E: Into<BoxedError>, + { + Error { + inner: Inner::Boxed(err.into()), + } + } + } + + impl error::Error for Error {} + + impl From<io::Error> for Error { + fn from(err: io::Error) -> Self { + Error::boxed(err) + } + } +} diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/log/src/kv/key.rs b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/log/src/kv/key.rs new file mode 100644 index 0000000..9a64b95 --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/log/src/kv/key.rs @@ -0,0 +1,143 @@ +//! Structured keys. + +use std::borrow::Borrow; +use std::fmt; + +/// A type that can be converted into a [`Key`](struct.Key.html). +pub trait ToKey { + /// Perform the conversion. + fn to_key(&self) -> Key; +} + +impl<'a, T> ToKey for &'a T +where + T: ToKey + ?Sized, +{ + fn to_key(&self) -> Key { + (**self).to_key() + } +} + +impl<'k> ToKey for Key<'k> { + fn to_key(&self) -> Key { + Key { key: self.key } + } +} + +impl ToKey for str { + fn to_key(&self) -> Key { + Key::from_str(self) + } +} + +/// A key in a key-value. +// These impls must only be based on the as_str() representation of the key +// If a new field (such as an optional index) is added to the key they must not affect comparison +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Key<'k> { + key: &'k str, +} + +impl<'k> Key<'k> { + /// Get a key from a borrowed string. + pub fn from_str(key: &'k str) -> Self { + Key { key } + } + + /// Get a borrowed string from this key. + pub fn as_str(&self) -> &str { + self.key + } +} + +impl<'k> fmt::Display for Key<'k> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.key.fmt(f) + } +} + +impl<'k> AsRef<str> for Key<'k> { + fn as_ref(&self) -> &str { + self.as_str() + } +} + +impl<'k> Borrow<str> for Key<'k> { + fn borrow(&self) -> &str { + self.as_str() + } +} + +impl<'k> From<&'k str> for Key<'k> { + fn from(s: &'k str) -> Self { + Key::from_str(s) + } +} + +#[cfg(feature = "std")] +mod std_support { + use super::*; + + use std::borrow::Cow; + + impl ToKey for String { + fn to_key(&self) -> Key { + Key::from_str(self) + } + } + + impl<'a> ToKey for Cow<'a, str> { + fn to_key(&self) -> Key { + Key::from_str(self) + } + } +} + +#[cfg(feature = "kv_sval")] +mod sval_support { + use super::*; + + use sval::Value; + use sval_ref::ValueRef; + + impl<'a> Value for Key<'a> { + fn stream<'sval, S: sval::Stream<'sval> + ?Sized>( + &'sval self, + stream: &mut S, + ) -> sval::Result { + self.key.stream(stream) + } + } + + impl<'a> ValueRef<'a> for Key<'a> { + fn stream_ref<S: sval::Stream<'a> + ?Sized>(&self, stream: &mut S) -> sval::Result { + self.key.stream(stream) + } + } +} + +#[cfg(feature = "kv_serde")] +mod serde_support { + use super::*; + + use serde::{Serialize, Serializer}; + + impl<'a> Serialize for Key<'a> { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: Serializer, + { + self.key.serialize(serializer) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn key_from_string() { + assert_eq!("a key", Key::from_str("a key").as_str()); + } +} diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/log/src/kv/mod.rs b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/log/src/kv/mod.rs new file mode 100644 index 0000000..1ccb825 --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/log/src/kv/mod.rs @@ -0,0 +1,265 @@ +//! Structured logging. +//! +//! Add the `kv` feature to your `Cargo.toml` to enable +//! this module: +//! +//! ```toml +//! [dependencies.log] +//! features = ["kv"] +//! ``` +//! +//! # Structured logging in `log` +//! +//! Structured logging enhances traditional text-based log records with user-defined +//! attributes. Structured logs can be analyzed using a variety of data processing +//! techniques, without needing to find and parse attributes from unstructured text first. +//! +//! In `log`, user-defined attributes are part of a [`Source`] on the log record. +//! Each attribute is a key-value; a pair of [`Key`] and [`Value`]. Keys are strings +//! and values are a datum of any type that can be formatted or serialized. Simple types +//! like strings, booleans, and numbers are supported, as well as arbitrarily complex +//! structures involving nested objects and sequences. +//! +//! ## Adding key-values to log records +//! +//! Key-values appear before the message format in the `log!` macros: +//! +//! ``` +//! # use log::info; +//! info!(a = 1; "Something of interest"); +//! ``` +//! +//! Key-values support the same shorthand identifer syntax as `format_args`: +//! +//! ``` +//! # use log::info; +//! let a = 1; +//! +//! info!(a; "Something of interest"); +//! ``` +//! +//! Values are capturing using the [`ToValue`] trait by default. To capture a value +//! using a different trait implementation, use a modifier after its key. Here's how +//! the same example can capture `a` using its `Debug` implementation instead: +//! +//! ``` +//! # use log::info; +//! info!(a:? = 1; "Something of interest"); +//! ``` +//! +//! The following capturing modifiers are supported: +//! +//! - `:?` will capture the value using `Debug`. +//! - `:debug` will capture the value using `Debug`. +//! - `:%` will capture the value using `Display`. +//! - `:display` will capture the value using `Display`. +//! - `:err` will capture the value using `std::error::Error` (requires the `kv_std` feature). +//! - `:sval` will capture the value using `sval::Value` (requires the `kv_sval` feature). +//! - `:serde` will capture the value using `serde::Serialize` (requires the `kv_serde` feature). +//! +//! ## Working with key-values on log records +//! +//! Use the [`Record::key_values`](../struct.Record.html#method.key_values) method to access key-values. +//! +//! Individual values can be pulled from the source by their key: +//! +//! ``` +//! # fn main() -> Result<(), log::kv::Error> { +//! use log::kv::{Source, Key, Value}; +//! # let record = log::Record::builder().key_values(&[("a", 1)]).build(); +//! +//! // info!(a = 1; "Something of interest"); +//! +//! let a: Value = record.key_values().get(Key::from("a")).unwrap(); +//! assert_eq!(1, a.to_i64().unwrap()); +//! # Ok(()) +//! # } +//! ``` +//! +//! All key-values can also be enumerated using a [`VisitSource`]: +//! +//! ``` +//! # fn main() -> Result<(), log::kv::Error> { +//! use std::collections::BTreeMap; +//! +//! use log::kv::{self, Source, Key, Value, VisitSource}; +//! +//! struct Collect<'kvs>(BTreeMap<Key<'kvs>, Value<'kvs>>); +//! +//! impl<'kvs> VisitSource<'kvs> for Collect<'kvs> { +//! fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), kv::Error> { +//! self.0.insert(key, value); +//! +//! Ok(()) +//! } +//! } +//! +//! let mut visitor = Collect(BTreeMap::new()); +//! +//! # let record = log::Record::builder().key_values(&[("a", 1), ("b", 2), ("c", 3)]).build(); +//! // info!(a = 1, b = 2, c = 3; "Something of interest"); +//! +//! record.key_values().visit(&mut visitor)?; +//! +//! let collected = visitor.0; +//! +//! assert_eq!( +//! vec!["a", "b", "c"], +//! collected +//! .keys() +//! .map(|k| k.as_str()) +//! .collect::<Vec<_>>(), +//! ); +//! # Ok(()) +//! # } +//! ``` +//! +//! [`Value`]s have methods for conversions to common types: +//! +//! ``` +//! # fn main() -> Result<(), log::kv::Error> { +//! use log::kv::{Source, Key}; +//! # let record = log::Record::builder().key_values(&[("a", 1)]).build(); +//! +//! // info!(a = 1; "Something of interest"); +//! +//! let a = record.key_values().get(Key::from("a")).unwrap(); +//! +//! assert_eq!(1, a.to_i64().unwrap()); +//! # Ok(()) +//! # } +//! ``` +//! +//! Values also have their own [`VisitValue`] type. Value visitors are a lightweight +//! API for working with primitives types: +//! +//! ``` +//! # fn main() -> Result<(), log::kv::Error> { +//! use log::kv::{self, Source, Key, VisitValue}; +//! # let record = log::Record::builder().key_values(&[("a", 1)]).build(); +//! +//! struct IsNumeric(bool); +//! +//! impl<'kvs> VisitValue<'kvs> for IsNumeric { +//! fn visit_any(&mut self, _value: kv::Value) -> Result<(), kv::Error> { +//! self.0 = false; +//! Ok(()) +//! } +//! +//! fn visit_u64(&mut self, _value: u64) -> Result<(), kv::Error> { +//! self.0 = true; +//! Ok(()) +//! } +//! +//! fn visit_i64(&mut self, _value: i64) -> Result<(), kv::Error> { +//! self.0 = true; +//! Ok(()) +//! } +//! +//! fn visit_u128(&mut self, _value: u128) -> Result<(), kv::Error> { +//! self.0 = true; +//! Ok(()) +//! } +//! +//! fn visit_i128(&mut self, _value: i128) -> Result<(), kv::Error> { +//! self.0 = true; +//! Ok(()) +//! } +//! +//! fn visit_f64(&mut self, _value: f64) -> Result<(), kv::Error> { +//! self.0 = true; +//! Ok(()) +//! } +//! } +//! +//! // info!(a = 1; "Something of interest"); +//! +//! let a = record.key_values().get(Key::from("a")).unwrap(); +//! +//! let mut visitor = IsNumeric(false); +//! +//! a.visit(&mut visitor)?; +//! +//! let is_numeric = visitor.0; +//! +//! assert!(is_numeric); +//! # Ok(()) +//! # } +//! ``` +//! +//! To serialize a value to a format like JSON, you can also use either `serde` or `sval`: +//! +//! ``` +//! # fn main() -> Result<(), Box<dyn std::error::Error>> { +//! # #[cfg(feature = "serde")] +//! # { +//! # use log::kv::Key; +//! #[derive(serde::Serialize)] +//! struct Data { +//! a: i32, b: bool, +//! c: &'static str, +//! } +//! +//! let data = Data { a: 1, b: true, c: "Some data" }; +//! +//! # let source = [("a", log::kv::Value::from_serde(&data))]; +//! # let record = log::Record::builder().key_values(&source).build(); +//! // info!(a = data; "Something of interest"); +//! +//! let a = record.key_values().get(Key::from("a")).unwrap(); +//! +//! assert_eq!("{\"a\":1,\"b\":true,\"c\":\"Some data\"}", serde_json::to_string(&a)?); +//! # } +//! # Ok(()) +//! # } +//! ``` +//! +//! The choice of serialization framework depends on the needs of the consumer. +//! If you're in a no-std environment, you can use `sval`. In other cases, you can use `serde`. +//! Log producers and log consumers don't need to agree on the serialization framework. +//! A value can be captured using its `serde::Serialize` implementation and still be serialized +//! through `sval` without losing any structure or data. +//! +//! Values can also always be formatted using the standard `Debug` and `Display` +//! traits: +//! +//! ``` +//! # use log::kv::Key; +//! # #[derive(Debug)] +//! struct Data { +//! a: i32, +//! b: bool, +//! c: &'static str, +//! } +//! +//! let data = Data { a: 1, b: true, c: "Some data" }; +//! +//! # let source = [("a", log::kv::Value::from_debug(&data))]; +//! # let record = log::Record::builder().key_values(&source).build(); +//! // info!(a = data; "Something of interest"); +//! +//! let a = record.key_values().get(Key::from("a")).unwrap(); +//! +//! assert_eq!("Data { a: 1, b: true, c: \"Some data\" }", format!("{a:?}")); +//! ``` + +mod error; +mod key; + +#[cfg(not(feature = "kv_unstable"))] +mod source; +#[cfg(not(feature = "kv_unstable"))] +mod value; + +pub use self::error::Error; +pub use self::key::{Key, ToKey}; +pub use self::source::{Source, VisitSource}; +pub use self::value::{ToValue, Value, VisitValue}; + +#[cfg(feature = "kv_unstable")] +pub mod source; +#[cfg(feature = "kv_unstable")] +pub mod value; + +#[cfg(feature = "kv_unstable")] +pub use self::source::Visitor; diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/log/src/kv/source.rs b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/log/src/kv/source.rs new file mode 100644 index 0000000..f463e6d --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/log/src/kv/source.rs @@ -0,0 +1,514 @@ +//! Sources for key-values. +//! +//! This module defines the [`Source`] type and supporting APIs for +//! working with collections of key-values. + +use crate::kv::{Error, Key, ToKey, ToValue, Value}; +use std::fmt; + +/// A source of key-values. +/// +/// The source may be a single pair, a set of pairs, or a filter over a set of pairs. +/// Use the [`VisitSource`](trait.VisitSource.html) trait to inspect the structured data +/// in a source. +/// +/// A source is like an iterator over its key-values, except with a push-based API +/// instead of a pull-based one. +/// +/// # Examples +/// +/// Enumerating the key-values in a source: +/// +/// ``` +/// # fn main() -> Result<(), log::kv::Error> { +/// use log::kv::{self, Source, Key, Value, VisitSource}; +/// +/// // A `VisitSource` that prints all key-values +/// // VisitSources are fed the key-value pairs of each key-values +/// struct Printer; +/// +/// impl<'kvs> VisitSource<'kvs> for Printer { +/// fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), kv::Error> { +/// println!("{key}: {value}"); +/// +/// Ok(()) +/// } +/// } +/// +/// // A source with 3 key-values +/// // Common collection types implement the `Source` trait +/// let source = &[ +/// ("a", 1), +/// ("b", 2), +/// ("c", 3), +/// ]; +/// +/// // Pass an instance of the `VisitSource` to a `Source` to visit it +/// source.visit(&mut Printer)?; +/// # Ok(()) +/// # } +/// ``` +pub trait Source { + /// Visit key-values. + /// + /// A source doesn't have to guarantee any ordering or uniqueness of key-values. + /// If the given visitor returns an error then the source may early-return with it, + /// even if there are more key-values. + /// + /// # Implementation notes + /// + /// A source should yield the same key-values to a subsequent visitor unless + /// that visitor itself fails. + fn visit<'kvs>(&'kvs self, visitor: &mut dyn VisitSource<'kvs>) -> Result<(), Error>; + + /// Get the value for a given key. + /// + /// If the key appears multiple times in the source then which key is returned + /// is implementation specific. + /// + /// # Implementation notes + /// + /// A source that can provide a more efficient implementation of this method + /// should override it. + fn get(&self, key: Key) -> Option<Value<'_>> { + get_default(self, key) + } + + /// Count the number of key-values that can be visited. + /// + /// # Implementation notes + /// + /// A source that knows the number of key-values upfront may provide a more + /// efficient implementation. + /// + /// A subsequent call to `visit` should yield the same number of key-values. + fn count(&self) -> usize { + count_default(self) + } +} + +/// The default implementation of `Source::get` +fn get_default<'v>(source: &'v (impl Source + ?Sized), key: Key) -> Option<Value<'v>> { + struct Get<'k, 'v> { + key: Key<'k>, + found: Option<Value<'v>>, + } + + impl<'k, 'kvs> VisitSource<'kvs> for Get<'k, 'kvs> { + fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error> { + if self.key == key { + self.found = Some(value); + } + + Ok(()) + } + } + + let mut get = Get { key, found: None }; + + let _ = source.visit(&mut get); + get.found +} + +/// The default implementation of `Source::count`. +fn count_default(source: impl Source) -> usize { + struct Count(usize); + + impl<'kvs> VisitSource<'kvs> for Count { + fn visit_pair(&mut self, _: Key<'kvs>, _: Value<'kvs>) -> Result<(), Error> { + self.0 += 1; + + Ok(()) + } + } + + let mut count = Count(0); + let _ = source.visit(&mut count); + count.0 +} + +impl<'a, T> Source for &'a T +where + T: Source + ?Sized, +{ + fn visit<'kvs>(&'kvs self, visitor: &mut dyn VisitSource<'kvs>) -> Result<(), Error> { + Source::visit(&**self, visitor) + } + + fn get(&self, key: Key) -> Option<Value<'_>> { + Source::get(&**self, key) + } + + fn count(&self) -> usize { + Source::count(&**self) + } +} + +impl<K, V> Source for (K, V) +where + K: ToKey, + V: ToValue, +{ + fn visit<'kvs>(&'kvs self, visitor: &mut dyn VisitSource<'kvs>) -> Result<(), Error> { + visitor.visit_pair(self.0.to_key(), self.1.to_value()) + } + + fn get(&self, key: Key) -> Option<Value<'_>> { + if self.0.to_key() == key { + Some(self.1.to_value()) + } else { + None + } + } + + fn count(&self) -> usize { + 1 + } +} + +impl<S> Source for [S] +where + S: Source, +{ + fn visit<'kvs>(&'kvs self, visitor: &mut dyn VisitSource<'kvs>) -> Result<(), Error> { + for source in self { + source.visit(visitor)?; + } + + Ok(()) + } + + fn get(&self, key: Key) -> Option<Value<'_>> { + for source in self { + if let Some(found) = source.get(key.clone()) { + return Some(found); + } + } + + None + } + + fn count(&self) -> usize { + self.iter().map(Source::count).sum() + } +} + +impl<const N: usize, S> Source for [S; N] +where + S: Source, +{ + fn visit<'kvs>(&'kvs self, visitor: &mut dyn VisitSource<'kvs>) -> Result<(), Error> { + Source::visit(self as &[_], visitor) + } + + fn get(&self, key: Key) -> Option<Value<'_>> { + Source::get(self as &[_], key) + } + + fn count(&self) -> usize { + Source::count(self as &[_]) + } +} + +impl<S> Source for Option<S> +where + S: Source, +{ + fn visit<'kvs>(&'kvs self, visitor: &mut dyn VisitSource<'kvs>) -> Result<(), Error> { + if let Some(source) = self { + source.visit(visitor)?; + } + + Ok(()) + } + + fn get(&self, key: Key) -> Option<Value<'_>> { + self.as_ref().and_then(|s| s.get(key)) + } + + fn count(&self) -> usize { + self.as_ref().map_or(0, Source::count) + } +} + +/// A visitor for the key-value pairs in a [`Source`](trait.Source.html). +pub trait VisitSource<'kvs> { + /// Visit a key-value pair. + fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error>; +} + +impl<'a, 'kvs, T> VisitSource<'kvs> for &'a mut T +where + T: VisitSource<'kvs> + ?Sized, +{ + fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error> { + (**self).visit_pair(key, value) + } +} + +impl<'a, 'b: 'a, 'kvs> VisitSource<'kvs> for fmt::DebugMap<'a, 'b> { + fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error> { + self.entry(&key, &value); + Ok(()) + } +} + +impl<'a, 'b: 'a, 'kvs> VisitSource<'kvs> for fmt::DebugList<'a, 'b> { + fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error> { + self.entry(&(key, value)); + Ok(()) + } +} + +impl<'a, 'b: 'a, 'kvs> VisitSource<'kvs> for fmt::DebugSet<'a, 'b> { + fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error> { + self.entry(&(key, value)); + Ok(()) + } +} + +impl<'a, 'b: 'a, 'kvs> VisitSource<'kvs> for fmt::DebugTuple<'a, 'b> { + fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error> { + self.field(&key); + self.field(&value); + Ok(()) + } +} + +#[cfg(feature = "std")] +mod std_support { + use super::*; + use std::borrow::Borrow; + use std::collections::{BTreeMap, HashMap}; + use std::hash::{BuildHasher, Hash}; + use std::rc::Rc; + use std::sync::Arc; + + impl<S> Source for Box<S> + where + S: Source + ?Sized, + { + fn visit<'kvs>(&'kvs self, visitor: &mut dyn VisitSource<'kvs>) -> Result<(), Error> { + Source::visit(&**self, visitor) + } + + fn get(&self, key: Key) -> Option<Value<'_>> { + Source::get(&**self, key) + } + + fn count(&self) -> usize { + Source::count(&**self) + } + } + + impl<S> Source for Arc<S> + where + S: Source + ?Sized, + { + fn visit<'kvs>(&'kvs self, visitor: &mut dyn VisitSource<'kvs>) -> Result<(), Error> { + Source::visit(&**self, visitor) + } + + fn get(&self, key: Key) -> Option<Value<'_>> { + Source::get(&**self, key) + } + + fn count(&self) -> usize { + Source::count(&**self) + } + } + + impl<S> Source for Rc<S> + where + S: Source + ?Sized, + { + fn visit<'kvs>(&'kvs self, visitor: &mut dyn VisitSource<'kvs>) -> Result<(), Error> { + Source::visit(&**self, visitor) + } + + fn get(&self, key: Key) -> Option<Value<'_>> { + Source::get(&**self, key) + } + + fn count(&self) -> usize { + Source::count(&**self) + } + } + + impl<S> Source for Vec<S> + where + S: Source, + { + fn visit<'kvs>(&'kvs self, visitor: &mut dyn VisitSource<'kvs>) -> Result<(), Error> { + Source::visit(&**self, visitor) + } + + fn get(&self, key: Key) -> Option<Value<'_>> { + Source::get(&**self, key) + } + + fn count(&self) -> usize { + Source::count(&**self) + } + } + + impl<'kvs, V> VisitSource<'kvs> for Box<V> + where + V: VisitSource<'kvs> + ?Sized, + { + fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error> { + (**self).visit_pair(key, value) + } + } + + impl<K, V, S> Source for HashMap<K, V, S> + where + K: ToKey + Borrow<str> + Eq + Hash, + V: ToValue, + S: BuildHasher, + { + fn visit<'kvs>(&'kvs self, visitor: &mut dyn VisitSource<'kvs>) -> Result<(), Error> { + for (key, value) in self { + visitor.visit_pair(key.to_key(), value.to_value())?; + } + Ok(()) + } + + fn get(&self, key: Key) -> Option<Value<'_>> { + HashMap::get(self, key.as_str()).map(|v| v.to_value()) + } + + fn count(&self) -> usize { + self.len() + } + } + + impl<K, V> Source for BTreeMap<K, V> + where + K: ToKey + Borrow<str> + Ord, + V: ToValue, + { + fn visit<'kvs>(&'kvs self, visitor: &mut dyn VisitSource<'kvs>) -> Result<(), Error> { + for (key, value) in self { + visitor.visit_pair(key.to_key(), value.to_value())?; + } + Ok(()) + } + + fn get(&self, key: Key) -> Option<Value<'_>> { + BTreeMap::get(self, key.as_str()).map(|v| v.to_value()) + } + + fn count(&self) -> usize { + self.len() + } + } + + #[cfg(test)] + mod tests { + use crate::kv::value; + + use super::*; + + #[test] + fn count() { + assert_eq!(1, Source::count(&Box::new(("a", 1)))); + assert_eq!(2, Source::count(&vec![("a", 1), ("b", 2)])); + } + + #[test] + fn get() { + let source = vec![("a", 1), ("b", 2), ("a", 1)]; + assert_eq!( + value::inner::Token::I64(1), + Source::get(&source, Key::from_str("a")).unwrap().to_token() + ); + + let source = Box::new(None::<(&str, i32)>); + assert!(Source::get(&source, Key::from_str("a")).is_none()); + } + + #[test] + fn hash_map() { + let mut map = HashMap::new(); + map.insert("a", 1); + map.insert("b", 2); + + assert_eq!(2, Source::count(&map)); + assert_eq!( + value::inner::Token::I64(1), + Source::get(&map, Key::from_str("a")).unwrap().to_token() + ); + } + + #[test] + fn btree_map() { + let mut map = BTreeMap::new(); + map.insert("a", 1); + map.insert("b", 2); + + assert_eq!(2, Source::count(&map)); + assert_eq!( + value::inner::Token::I64(1), + Source::get(&map, Key::from_str("a")).unwrap().to_token() + ); + } + } +} + +// NOTE: Deprecated; but aliases can't carry this attribute +#[cfg(feature = "kv_unstable")] +pub use VisitSource as Visitor; + +#[cfg(test)] +mod tests { + use crate::kv::value; + + use super::*; + + #[test] + fn source_is_object_safe() { + fn _check(_: &dyn Source) {} + } + + #[test] + fn visitor_is_object_safe() { + fn _check(_: &dyn VisitSource) {} + } + + #[test] + fn count() { + struct OnePair { + key: &'static str, + value: i32, + } + + impl Source for OnePair { + fn visit<'kvs>(&'kvs self, visitor: &mut dyn VisitSource<'kvs>) -> Result<(), Error> { + visitor.visit_pair(self.key.to_key(), self.value.to_value()) + } + } + + assert_eq!(1, Source::count(&("a", 1))); + assert_eq!(2, Source::count(&[("a", 1), ("b", 2)] as &[_])); + assert_eq!(0, Source::count(&None::<(&str, i32)>)); + assert_eq!(1, Source::count(&OnePair { key: "a", value: 1 })); + } + + #[test] + fn get() { + let source = &[("a", 1), ("b", 2), ("a", 1)] as &[_]; + assert_eq!( + value::inner::Token::I64(1), + Source::get(source, Key::from_str("a")).unwrap().to_token() + ); + assert_eq!( + value::inner::Token::I64(2), + Source::get(source, Key::from_str("b")).unwrap().to_token() + ); + assert!(Source::get(&source, Key::from_str("c")).is_none()); + + let source = None::<(&str, i32)>; + assert!(Source::get(&source, Key::from_str("a")).is_none()); + } +} diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/log/src/kv/value.rs b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/log/src/kv/value.rs new file mode 100644 index 0000000..1511dd0 --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/log/src/kv/value.rs @@ -0,0 +1,1394 @@ +//! Structured values. +//! +//! This module defines the [`Value`] type and supporting APIs for +//! capturing and serializing them. + +use std::fmt; + +pub use crate::kv::Error; + +/// A type that can be converted into a [`Value`](struct.Value.html). +pub trait ToValue { + /// Perform the conversion. + fn to_value(&self) -> Value; +} + +impl<'a, T> ToValue for &'a T +where + T: ToValue + ?Sized, +{ + fn to_value(&self) -> Value { + (**self).to_value() + } +} + +impl<'v> ToValue for Value<'v> { + fn to_value(&self) -> Value { + Value { + inner: self.inner.clone(), + } + } +} + +/// A value in a key-value. +/// +/// Values are an anonymous bag containing some structured datum. +/// +/// # Capturing values +/// +/// There are a few ways to capture a value: +/// +/// - Using the `Value::from_*` methods. +/// - Using the `ToValue` trait. +/// - Using the standard `From` trait. +/// +/// ## Using the `Value::from_*` methods +/// +/// `Value` offers a few constructor methods that capture values of different kinds. +/// +/// ``` +/// use log::kv::Value; +/// +/// let value = Value::from_debug(&42i32); +/// +/// assert_eq!(None, value.to_i64()); +/// ``` +/// +/// ## Using the `ToValue` trait +/// +/// The `ToValue` trait can be used to capture values generically. +/// It's the bound used by `Source`. +/// +/// ``` +/// # use log::kv::ToValue; +/// let value = 42i32.to_value(); +/// +/// assert_eq!(Some(42), value.to_i64()); +/// ``` +/// +/// ## Using the standard `From` trait +/// +/// Standard types that implement `ToValue` also implement `From`. +/// +/// ``` +/// use log::kv::Value; +/// +/// let value = Value::from(42i32); +/// +/// assert_eq!(Some(42), value.to_i64()); +/// ``` +/// +/// # Data model +/// +/// Values can hold one of a number of types: +/// +/// - **Null:** The absence of any other meaningful value. Note that +/// `Some(Value::null())` is not the same as `None`. The former is +/// `null` while the latter is `undefined`. This is important to be +/// able to tell the difference between a key-value that was logged, +/// but its value was empty (`Some(Value::null())`) and a key-value +/// that was never logged at all (`None`). +/// - **Strings:** `str`, `char`. +/// - **Booleans:** `bool`. +/// - **Integers:** `u8`-`u128`, `i8`-`i128`, `NonZero*`. +/// - **Floating point numbers:** `f32`-`f64`. +/// - **Errors:** `dyn (Error + 'static)`. +/// - **`serde`:** Any type in `serde`'s data model. +/// - **`sval`:** Any type in `sval`'s data model. +/// +/// # Serialization +/// +/// Values provide a number of ways to be serialized. +/// +/// For basic types the [`Value::visit`] method can be used to extract the +/// underlying typed value. However this is limited in the amount of types +/// supported (see the [`VisitValue`] trait methods). +/// +/// For more complex types one of the following traits can be used: +/// * `sval::Value`, requires the `kv_sval` feature. +/// * `serde::Serialize`, requires the `kv_serde` feature. +/// +/// You don't need a visitor to serialize values through `serde` or `sval`. +/// +/// A value can always be serialized using any supported framework, regardless +/// of how it was captured. If, for example, a value was captured using its +/// `Display` implementation, it will serialize through `serde` as a string. If it was +/// captured as a struct using `serde`, it will also serialize as a struct +/// through `sval`, or can be formatted using a `Debug`-compatible representation. +pub struct Value<'v> { + inner: inner::Inner<'v>, +} + +impl<'v> Value<'v> { + /// Get a value from a type implementing `ToValue`. + pub fn from_any<T>(value: &'v T) -> Self + where + T: ToValue, + { + value.to_value() + } + + /// Get a value from a type implementing `std::fmt::Debug`. + pub fn from_debug<T>(value: &'v T) -> Self + where + T: fmt::Debug, + { + Value { + inner: inner::Inner::from_debug(value), + } + } + + /// Get a value from a type implementing `std::fmt::Display`. + pub fn from_display<T>(value: &'v T) -> Self + where + T: fmt::Display, + { + Value { + inner: inner::Inner::from_display(value), + } + } + + /// Get a value from a type implementing `serde::Serialize`. + #[cfg(feature = "kv_serde")] + pub fn from_serde<T>(value: &'v T) -> Self + where + T: serde::Serialize, + { + Value { + inner: inner::Inner::from_serde1(value), + } + } + + /// Get a value from a type implementing `sval::Value`. + #[cfg(feature = "kv_sval")] + pub fn from_sval<T>(value: &'v T) -> Self + where + T: sval::Value, + { + Value { + inner: inner::Inner::from_sval2(value), + } + } + + /// Get a value from a dynamic `std::fmt::Debug`. + pub fn from_dyn_debug(value: &'v dyn fmt::Debug) -> Self { + Value { + inner: inner::Inner::from_dyn_debug(value), + } + } + + /// Get a value from a dynamic `std::fmt::Display`. + pub fn from_dyn_display(value: &'v dyn fmt::Display) -> Self { + Value { + inner: inner::Inner::from_dyn_display(value), + } + } + + /// Get a value from a dynamic error. + #[cfg(feature = "kv_std")] + pub fn from_dyn_error(err: &'v (dyn std::error::Error + 'static)) -> Self { + Value { + inner: inner::Inner::from_dyn_error(err), + } + } + + /// Get a `null` value. + pub fn null() -> Self { + Value { + inner: inner::Inner::empty(), + } + } + + /// Get a value from an internal primitive. + fn from_inner<T>(value: T) -> Self + where + T: Into<inner::Inner<'v>>, + { + Value { + inner: value.into(), + } + } + + /// Inspect this value using a simple visitor. + /// + /// When the `kv_serde` or `kv_sval` features are enabled, you can also + /// serialize a value using its `Serialize` or `Value` implementation. + pub fn visit(&self, visitor: impl VisitValue<'v>) -> Result<(), Error> { + inner::visit(&self.inner, visitor) + } +} + +impl<'v> fmt::Debug for Value<'v> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(&self.inner, f) + } +} + +impl<'v> fmt::Display for Value<'v> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(&self.inner, f) + } +} + +#[cfg(feature = "kv_serde")] +impl<'v> serde::Serialize for Value<'v> { + fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error> + where + S: serde::Serializer, + { + self.inner.serialize(s) + } +} + +#[cfg(feature = "kv_sval")] +impl<'v> sval::Value for Value<'v> { + fn stream<'sval, S: sval::Stream<'sval> + ?Sized>(&'sval self, stream: &mut S) -> sval::Result { + sval::Value::stream(&self.inner, stream) + } +} + +#[cfg(feature = "kv_sval")] +impl<'v> sval_ref::ValueRef<'v> for Value<'v> { + fn stream_ref<S: sval::Stream<'v> + ?Sized>(&self, stream: &mut S) -> sval::Result { + sval_ref::ValueRef::stream_ref(&self.inner, stream) + } +} + +impl ToValue for str { + fn to_value(&self) -> Value { + Value::from(self) + } +} + +impl<'v> From<&'v str> for Value<'v> { + fn from(value: &'v str) -> Self { + Value::from_inner(value) + } +} + +impl ToValue for () { + fn to_value(&self) -> Value { + Value::from_inner(()) + } +} + +impl<T> ToValue for Option<T> +where + T: ToValue, +{ + fn to_value(&self) -> Value { + match *self { + Some(ref value) => value.to_value(), + None => Value::from_inner(()), + } + } +} + +macro_rules! impl_to_value_primitive { + ($($into_ty:ty,)*) => { + $( + impl ToValue for $into_ty { + fn to_value(&self) -> Value { + Value::from(*self) + } + } + + impl<'v> From<$into_ty> for Value<'v> { + fn from(value: $into_ty) -> Self { + Value::from_inner(value) + } + } + + impl<'v> From<&'v $into_ty> for Value<'v> { + fn from(value: &'v $into_ty) -> Self { + Value::from_inner(*value) + } + } + )* + }; +} + +macro_rules! impl_to_value_nonzero_primitive { + ($($into_ty:ident,)*) => { + $( + impl ToValue for std::num::$into_ty { + fn to_value(&self) -> Value { + Value::from(self.get()) + } + } + + impl<'v> From<std::num::$into_ty> for Value<'v> { + fn from(value: std::num::$into_ty) -> Self { + Value::from(value.get()) + } + } + + impl<'v> From<&'v std::num::$into_ty> for Value<'v> { + fn from(value: &'v std::num::$into_ty) -> Self { + Value::from(value.get()) + } + } + )* + }; +} + +macro_rules! impl_value_to_primitive { + ($(#[doc = $doc:tt] $into_name:ident -> $into_ty:ty,)*) => { + impl<'v> Value<'v> { + $( + #[doc = $doc] + pub fn $into_name(&self) -> Option<$into_ty> { + self.inner.$into_name() + } + )* + } + } +} + +impl_to_value_primitive![ + usize, u8, u16, u32, u64, u128, isize, i8, i16, i32, i64, i128, f32, f64, char, bool, +]; + +#[rustfmt::skip] +impl_to_value_nonzero_primitive![ + NonZeroUsize, NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, + NonZeroIsize, NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, +]; + +impl_value_to_primitive![ + #[doc = "Try convert this value into a `u64`."] + to_u64 -> u64, + #[doc = "Try convert this value into a `i64`."] + to_i64 -> i64, + #[doc = "Try convert this value into a `u128`."] + to_u128 -> u128, + #[doc = "Try convert this value into a `i128`."] + to_i128 -> i128, + #[doc = "Try convert this value into a `f64`."] + to_f64 -> f64, + #[doc = "Try convert this value into a `char`."] + to_char -> char, + #[doc = "Try convert this value into a `bool`."] + to_bool -> bool, +]; + +impl<'v> Value<'v> { + /// Try convert this value into an error. + #[cfg(feature = "kv_std")] + pub fn to_borrowed_error(&self) -> Option<&(dyn std::error::Error + 'static)> { + self.inner.to_borrowed_error() + } + + /// Try convert this value into a borrowed string. + pub fn to_borrowed_str(&self) -> Option<&str> { + self.inner.to_borrowed_str() + } +} + +#[cfg(feature = "kv_std")] +mod std_support { + use std::borrow::Cow; + use std::rc::Rc; + use std::sync::Arc; + + use super::*; + + impl<T> ToValue for Box<T> + where + T: ToValue + ?Sized, + { + fn to_value(&self) -> Value { + (**self).to_value() + } + } + + impl<T> ToValue for Arc<T> + where + T: ToValue + ?Sized, + { + fn to_value(&self) -> Value { + (**self).to_value() + } + } + + impl<T> ToValue for Rc<T> + where + T: ToValue + ?Sized, + { + fn to_value(&self) -> Value { + (**self).to_value() + } + } + + impl ToValue for String { + fn to_value(&self) -> Value { + Value::from(&**self) + } + } + + impl<'v> ToValue for Cow<'v, str> { + fn to_value(&self) -> Value { + Value::from(&**self) + } + } + + impl<'v> Value<'v> { + /// Try convert this value into a string. + pub fn to_cow_str(&self) -> Option<Cow<'v, str>> { + self.inner.to_str() + } + } + + impl<'v> From<&'v String> for Value<'v> { + fn from(v: &'v String) -> Self { + Value::from(&**v) + } + } +} + +/// A visitor for a [`Value`]. +/// +/// Also see [`Value`'s documentation on seralization]. Value visitors are a simple alternative +/// to a more fully-featured serialization framework like `serde` or `sval`. A value visitor +/// can differentiate primitive types through methods like [`VisitValue::visit_bool`] and +/// [`VisitValue::visit_str`], but more complex types like maps and sequences +/// will fallthrough to [`VisitValue::visit_any`]. +/// +/// If you're trying to serialize a value to a format like JSON, you can use either `serde` +/// or `sval` directly with the value. You don't need a visitor. +/// +/// [`Value`'s documentation on seralization]: Value#serialization +pub trait VisitValue<'v> { + /// Visit a `Value`. + /// + /// This is the only required method on `VisitValue` and acts as a fallback for any + /// more specific methods that aren't overridden. + /// The `Value` may be formatted using its `fmt::Debug` or `fmt::Display` implementation, + /// or serialized using its `sval::Value` or `serde::Serialize` implementation. + fn visit_any(&mut self, value: Value) -> Result<(), Error>; + + /// Visit an empty value. + fn visit_null(&mut self) -> Result<(), Error> { + self.visit_any(Value::null()) + } + + /// Visit an unsigned integer. + fn visit_u64(&mut self, value: u64) -> Result<(), Error> { + self.visit_any(value.into()) + } + + /// Visit a signed integer. + fn visit_i64(&mut self, value: i64) -> Result<(), Error> { + self.visit_any(value.into()) + } + + /// Visit a big unsigned integer. + fn visit_u128(&mut self, value: u128) -> Result<(), Error> { + self.visit_any((value).into()) + } + + /// Visit a big signed integer. + fn visit_i128(&mut self, value: i128) -> Result<(), Error> { + self.visit_any((value).into()) + } + + /// Visit a floating point. + fn visit_f64(&mut self, value: f64) -> Result<(), Error> { + self.visit_any(value.into()) + } + + /// Visit a boolean. + fn visit_bool(&mut self, value: bool) -> Result<(), Error> { + self.visit_any(value.into()) + } + + /// Visit a string. + fn visit_str(&mut self, value: &str) -> Result<(), Error> { + self.visit_any(value.into()) + } + + /// Visit a string. + fn visit_borrowed_str(&mut self, value: &'v str) -> Result<(), Error> { + self.visit_str(value) + } + + /// Visit a Unicode character. + fn visit_char(&mut self, value: char) -> Result<(), Error> { + let mut b = [0; 4]; + self.visit_str(&*value.encode_utf8(&mut b)) + } + + /// Visit an error. + #[cfg(feature = "kv_std")] + fn visit_error(&mut self, err: &(dyn std::error::Error + 'static)) -> Result<(), Error> { + self.visit_any(Value::from_dyn_error(err)) + } + + /// Visit an error. + #[cfg(feature = "kv_std")] + fn visit_borrowed_error( + &mut self, + err: &'v (dyn std::error::Error + 'static), + ) -> Result<(), Error> { + self.visit_any(Value::from_dyn_error(err)) + } +} + +impl<'a, 'v, T: ?Sized> VisitValue<'v> for &'a mut T +where + T: VisitValue<'v>, +{ + fn visit_any(&mut self, value: Value) -> Result<(), Error> { + (**self).visit_any(value) + } + + fn visit_null(&mut self) -> Result<(), Error> { + (**self).visit_null() + } + + fn visit_u64(&mut self, value: u64) -> Result<(), Error> { + (**self).visit_u64(value) + } + + fn visit_i64(&mut self, value: i64) -> Result<(), Error> { + (**self).visit_i64(value) + } + + fn visit_u128(&mut self, value: u128) -> Result<(), Error> { + (**self).visit_u128(value) + } + + fn visit_i128(&mut self, value: i128) -> Result<(), Error> { + (**self).visit_i128(value) + } + + fn visit_f64(&mut self, value: f64) -> Result<(), Error> { + (**self).visit_f64(value) + } + + fn visit_bool(&mut self, value: bool) -> Result<(), Error> { + (**self).visit_bool(value) + } + + fn visit_str(&mut self, value: &str) -> Result<(), Error> { + (**self).visit_str(value) + } + + fn visit_borrowed_str(&mut self, value: &'v str) -> Result<(), Error> { + (**self).visit_borrowed_str(value) + } + + fn visit_char(&mut self, value: char) -> Result<(), Error> { + (**self).visit_char(value) + } + + #[cfg(feature = "kv_std")] + fn visit_error(&mut self, err: &(dyn std::error::Error + 'static)) -> Result<(), Error> { + (**self).visit_error(err) + } + + #[cfg(feature = "kv_std")] + fn visit_borrowed_error( + &mut self, + err: &'v (dyn std::error::Error + 'static), + ) -> Result<(), Error> { + (**self).visit_borrowed_error(err) + } +} + +#[cfg(feature = "value-bag")] +pub(in crate::kv) mod inner { + /** + An implementation of `Value` based on a library called `value_bag`. + + `value_bag` was written specifically for use in `log`'s value, but was split out when it outgrew + the codebase here. It's a general-purpose type-erasure library that handles mapping between + more fully-featured serialization frameworks. + */ + use super::*; + + pub use value_bag::ValueBag as Inner; + + pub use value_bag::Error; + + #[cfg(test)] + pub use value_bag::test::TestToken as Token; + + pub fn visit<'v>( + inner: &Inner<'v>, + visitor: impl VisitValue<'v>, + ) -> Result<(), crate::kv::Error> { + struct InnerVisitValue<V>(V); + + impl<'v, V> value_bag::visit::Visit<'v> for InnerVisitValue<V> + where + V: VisitValue<'v>, + { + fn visit_any(&mut self, value: value_bag::ValueBag) -> Result<(), Error> { + self.0 + .visit_any(Value { inner: value }) + .map_err(crate::kv::Error::into_value) + } + + fn visit_empty(&mut self) -> Result<(), Error> { + self.0.visit_null().map_err(crate::kv::Error::into_value) + } + + fn visit_u64(&mut self, value: u64) -> Result<(), Error> { + self.0 + .visit_u64(value) + .map_err(crate::kv::Error::into_value) + } + + fn visit_i64(&mut self, value: i64) -> Result<(), Error> { + self.0 + .visit_i64(value) + .map_err(crate::kv::Error::into_value) + } + + fn visit_u128(&mut self, value: u128) -> Result<(), Error> { + self.0 + .visit_u128(value) + .map_err(crate::kv::Error::into_value) + } + + fn visit_i128(&mut self, value: i128) -> Result<(), Error> { + self.0 + .visit_i128(value) + .map_err(crate::kv::Error::into_value) + } + + fn visit_f64(&mut self, value: f64) -> Result<(), Error> { + self.0 + .visit_f64(value) + .map_err(crate::kv::Error::into_value) + } + + fn visit_bool(&mut self, value: bool) -> Result<(), Error> { + self.0 + .visit_bool(value) + .map_err(crate::kv::Error::into_value) + } + + fn visit_str(&mut self, value: &str) -> Result<(), Error> { + self.0 + .visit_str(value) + .map_err(crate::kv::Error::into_value) + } + + fn visit_borrowed_str(&mut self, value: &'v str) -> Result<(), Error> { + self.0 + .visit_borrowed_str(value) + .map_err(crate::kv::Error::into_value) + } + + fn visit_char(&mut self, value: char) -> Result<(), Error> { + self.0 + .visit_char(value) + .map_err(crate::kv::Error::into_value) + } + + #[cfg(feature = "kv_std")] + fn visit_error( + &mut self, + err: &(dyn std::error::Error + 'static), + ) -> Result<(), Error> { + self.0 + .visit_error(err) + .map_err(crate::kv::Error::into_value) + } + + #[cfg(feature = "kv_std")] + fn visit_borrowed_error( + &mut self, + err: &'v (dyn std::error::Error + 'static), + ) -> Result<(), Error> { + self.0 + .visit_borrowed_error(err) + .map_err(crate::kv::Error::into_value) + } + } + + inner + .visit(&mut InnerVisitValue(visitor)) + .map_err(crate::kv::Error::from_value) + } +} + +#[cfg(not(feature = "value-bag"))] +pub(in crate::kv) mod inner { + /** + This is a dependency-free implementation of `Value` when there's no serialization frameworks involved. + In these simple cases a more fully featured solution like `value_bag` isn't needed, so we avoid pulling it in. + + There are a few things here that need to remain consistent with the `value_bag`-based implementation: + + 1. Conversions should always produce the same results. If a conversion here returns `Some`, then + the same `value_bag`-based conversion must also. Of particular note here are floats to ints; they're + based on the standard library's `TryInto` conversions, which need to be convert to `i32` or `u32`, + and then to `f64`. + 2. VisitValues should always be called in the same way. If a particular type of value calls `visit_i64`, + then the same `value_bag`-based visitor must also. + */ + use super::*; + + #[derive(Clone)] + pub enum Inner<'v> { + None, + Bool(bool), + Str(&'v str), + Char(char), + I64(i64), + U64(u64), + F64(f64), + I128(i128), + U128(u128), + Debug(&'v dyn fmt::Debug), + Display(&'v dyn fmt::Display), + } + + impl<'v> From<()> for Inner<'v> { + fn from(_: ()) -> Self { + Inner::None + } + } + + impl<'v> From<bool> for Inner<'v> { + fn from(v: bool) -> Self { + Inner::Bool(v) + } + } + + impl<'v> From<char> for Inner<'v> { + fn from(v: char) -> Self { + Inner::Char(v) + } + } + + impl<'v> From<f32> for Inner<'v> { + fn from(v: f32) -> Self { + Inner::F64(v as f64) + } + } + + impl<'v> From<f64> for Inner<'v> { + fn from(v: f64) -> Self { + Inner::F64(v) + } + } + + impl<'v> From<i8> for Inner<'v> { + fn from(v: i8) -> Self { + Inner::I64(v as i64) + } + } + + impl<'v> From<i16> for Inner<'v> { + fn from(v: i16) -> Self { + Inner::I64(v as i64) + } + } + + impl<'v> From<i32> for Inner<'v> { + fn from(v: i32) -> Self { + Inner::I64(v as i64) + } + } + + impl<'v> From<i64> for Inner<'v> { + fn from(v: i64) -> Self { + Inner::I64(v as i64) + } + } + + impl<'v> From<isize> for Inner<'v> { + fn from(v: isize) -> Self { + Inner::I64(v as i64) + } + } + + impl<'v> From<u8> for Inner<'v> { + fn from(v: u8) -> Self { + Inner::U64(v as u64) + } + } + + impl<'v> From<u16> for Inner<'v> { + fn from(v: u16) -> Self { + Inner::U64(v as u64) + } + } + + impl<'v> From<u32> for Inner<'v> { + fn from(v: u32) -> Self { + Inner::U64(v as u64) + } + } + + impl<'v> From<u64> for Inner<'v> { + fn from(v: u64) -> Self { + Inner::U64(v as u64) + } + } + + impl<'v> From<usize> for Inner<'v> { + fn from(v: usize) -> Self { + Inner::U64(v as u64) + } + } + + impl<'v> From<i128> for Inner<'v> { + fn from(v: i128) -> Self { + Inner::I128(v) + } + } + + impl<'v> From<u128> for Inner<'v> { + fn from(v: u128) -> Self { + Inner::U128(v) + } + } + + impl<'v> From<&'v str> for Inner<'v> { + fn from(v: &'v str) -> Self { + Inner::Str(v) + } + } + + impl<'v> fmt::Debug for Inner<'v> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Inner::None => fmt::Debug::fmt(&None::<()>, f), + Inner::Bool(v) => fmt::Debug::fmt(v, f), + Inner::Str(v) => fmt::Debug::fmt(v, f), + Inner::Char(v) => fmt::Debug::fmt(v, f), + Inner::I64(v) => fmt::Debug::fmt(v, f), + Inner::U64(v) => fmt::Debug::fmt(v, f), + Inner::F64(v) => fmt::Debug::fmt(v, f), + Inner::I128(v) => fmt::Debug::fmt(v, f), + Inner::U128(v) => fmt::Debug::fmt(v, f), + Inner::Debug(v) => fmt::Debug::fmt(v, f), + Inner::Display(v) => fmt::Display::fmt(v, f), + } + } + } + + impl<'v> fmt::Display for Inner<'v> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Inner::None => fmt::Debug::fmt(&None::<()>, f), + Inner::Bool(v) => fmt::Display::fmt(v, f), + Inner::Str(v) => fmt::Display::fmt(v, f), + Inner::Char(v) => fmt::Display::fmt(v, f), + Inner::I64(v) => fmt::Display::fmt(v, f), + Inner::U64(v) => fmt::Display::fmt(v, f), + Inner::F64(v) => fmt::Display::fmt(v, f), + Inner::I128(v) => fmt::Display::fmt(v, f), + Inner::U128(v) => fmt::Display::fmt(v, f), + Inner::Debug(v) => fmt::Debug::fmt(v, f), + Inner::Display(v) => fmt::Display::fmt(v, f), + } + } + } + + impl<'v> Inner<'v> { + pub fn from_debug<T: fmt::Debug>(value: &'v T) -> Self { + Inner::Debug(value) + } + + pub fn from_display<T: fmt::Display>(value: &'v T) -> Self { + Inner::Display(value) + } + + pub fn from_dyn_debug(value: &'v dyn fmt::Debug) -> Self { + Inner::Debug(value) + } + + pub fn from_dyn_display(value: &'v dyn fmt::Display) -> Self { + Inner::Display(value) + } + + pub fn empty() -> Self { + Inner::None + } + + pub fn to_bool(&self) -> Option<bool> { + match self { + Inner::Bool(v) => Some(*v), + _ => None, + } + } + + pub fn to_char(&self) -> Option<char> { + match self { + Inner::Char(v) => Some(*v), + _ => None, + } + } + + pub fn to_f64(&self) -> Option<f64> { + match self { + Inner::F64(v) => Some(*v), + Inner::I64(v) => { + let v: i32 = (*v).try_into().ok()?; + v.try_into().ok() + } + Inner::U64(v) => { + let v: u32 = (*v).try_into().ok()?; + v.try_into().ok() + } + Inner::I128(v) => { + let v: i32 = (*v).try_into().ok()?; + v.try_into().ok() + } + Inner::U128(v) => { + let v: u32 = (*v).try_into().ok()?; + v.try_into().ok() + } + _ => None, + } + } + + pub fn to_i64(&self) -> Option<i64> { + match self { + Inner::I64(v) => Some(*v), + Inner::U64(v) => (*v).try_into().ok(), + Inner::I128(v) => (*v).try_into().ok(), + Inner::U128(v) => (*v).try_into().ok(), + _ => None, + } + } + + pub fn to_u64(&self) -> Option<u64> { + match self { + Inner::U64(v) => Some(*v), + Inner::I64(v) => (*v).try_into().ok(), + Inner::I128(v) => (*v).try_into().ok(), + Inner::U128(v) => (*v).try_into().ok(), + _ => None, + } + } + + pub fn to_u128(&self) -> Option<u128> { + match self { + Inner::U128(v) => Some(*v), + Inner::I64(v) => (*v).try_into().ok(), + Inner::U64(v) => (*v).try_into().ok(), + Inner::I128(v) => (*v).try_into().ok(), + _ => None, + } + } + + pub fn to_i128(&self) -> Option<i128> { + match self { + Inner::I128(v) => Some(*v), + Inner::I64(v) => (*v).try_into().ok(), + Inner::U64(v) => (*v).try_into().ok(), + Inner::U128(v) => (*v).try_into().ok(), + _ => None, + } + } + + pub fn to_borrowed_str(&self) -> Option<&'v str> { + match self { + Inner::Str(v) => Some(v), + _ => None, + } + } + + #[cfg(test)] + pub fn to_test_token(&self) -> Token { + match self { + Inner::None => Token::None, + Inner::Bool(v) => Token::Bool(*v), + Inner::Str(v) => Token::Str(*v), + Inner::Char(v) => Token::Char(*v), + Inner::I64(v) => Token::I64(*v), + Inner::U64(v) => Token::U64(*v), + Inner::F64(v) => Token::F64(*v), + Inner::I128(_) => unimplemented!(), + Inner::U128(_) => unimplemented!(), + Inner::Debug(_) => unimplemented!(), + Inner::Display(_) => unimplemented!(), + } + } + } + + #[cfg(test)] + #[derive(Debug, PartialEq)] + pub enum Token<'v> { + None, + Bool(bool), + Char(char), + Str(&'v str), + F64(f64), + I64(i64), + U64(u64), + } + + pub fn visit<'v>( + inner: &Inner<'v>, + mut visitor: impl VisitValue<'v>, + ) -> Result<(), crate::kv::Error> { + match inner { + Inner::None => visitor.visit_null(), + Inner::Bool(v) => visitor.visit_bool(*v), + Inner::Str(v) => visitor.visit_borrowed_str(*v), + Inner::Char(v) => visitor.visit_char(*v), + Inner::I64(v) => visitor.visit_i64(*v), + Inner::U64(v) => visitor.visit_u64(*v), + Inner::F64(v) => visitor.visit_f64(*v), + Inner::I128(v) => visitor.visit_i128(*v), + Inner::U128(v) => visitor.visit_u128(*v), + Inner::Debug(v) => visitor.visit_any(Value::from_dyn_debug(*v)), + Inner::Display(v) => visitor.visit_any(Value::from_dyn_display(*v)), + } + } +} + +impl<'v> Value<'v> { + /// Get a value from a type implementing `std::fmt::Debug`. + #[cfg(feature = "kv_unstable")] + #[deprecated(note = "use `from_debug` instead")] + pub fn capture_debug<T>(value: &'v T) -> Self + where + T: fmt::Debug + 'static, + { + Value::from_debug(value) + } + + /// Get a value from a type implementing `std::fmt::Display`. + #[cfg(feature = "kv_unstable")] + #[deprecated(note = "use `from_display` instead")] + pub fn capture_display<T>(value: &'v T) -> Self + where + T: fmt::Display + 'static, + { + Value::from_display(value) + } + + /// Get a value from an error. + #[cfg(feature = "kv_unstable_std")] + #[deprecated(note = "use `from_dyn_error` instead")] + pub fn capture_error<T>(err: &'v T) -> Self + where + T: std::error::Error + 'static, + { + Value::from_dyn_error(err) + } + + /// Get a value from a type implementing `serde::Serialize`. + #[cfg(feature = "kv_unstable_serde")] + #[deprecated(note = "use `from_serde` instead")] + pub fn capture_serde<T>(value: &'v T) -> Self + where + T: serde::Serialize + 'static, + { + Value::from_serde(value) + } + + /// Get a value from a type implementing `sval::Value`. + #[cfg(feature = "kv_unstable_sval")] + #[deprecated(note = "use `from_sval` instead")] + pub fn capture_sval<T>(value: &'v T) -> Self + where + T: sval::Value + 'static, + { + Value::from_sval(value) + } + + /// Check whether this value can be downcast to `T`. + #[cfg(feature = "kv_unstable")] + #[deprecated( + note = "downcasting has been removed; log an issue at https://github.com/rust-lang/log/issues if this is something you rely on" + )] + pub fn is<T: 'static>(&self) -> bool { + false + } + + /// Try downcast this value to `T`. + #[cfg(feature = "kv_unstable")] + #[deprecated( + note = "downcasting has been removed; log an issue at https://github.com/rust-lang/log/issues if this is something you rely on" + )] + pub fn downcast_ref<T: 'static>(&self) -> Option<&T> { + None + } +} + +// NOTE: Deprecated; but aliases can't carry this attribute +#[cfg(feature = "kv_unstable")] +pub use VisitValue as Visit; + +/// Get a value from a type implementing `std::fmt::Debug`. +#[cfg(feature = "kv_unstable")] +#[deprecated(note = "use the `key:? = value` macro syntax instead")] +#[macro_export] +macro_rules! as_debug { + ($capture:expr) => { + $crate::kv::Value::from_debug(&$capture) + }; +} + +/// Get a value from a type implementing `std::fmt::Display`. +#[cfg(feature = "kv_unstable")] +#[deprecated(note = "use the `key:% = value` macro syntax instead")] +#[macro_export] +macro_rules! as_display { + ($capture:expr) => { + $crate::kv::Value::from_display(&$capture) + }; +} + +/// Get a value from an error. +#[cfg(feature = "kv_unstable_std")] +#[deprecated(note = "use the `key:err = value` macro syntax instead")] +#[macro_export] +macro_rules! as_error { + ($capture:expr) => { + $crate::kv::Value::from_dyn_error(&$capture) + }; +} + +#[cfg(feature = "kv_unstable_serde")] +#[deprecated(note = "use the `key:serde = value` macro syntax instead")] +/// Get a value from a type implementing `serde::Serialize`. +#[macro_export] +macro_rules! as_serde { + ($capture:expr) => { + $crate::kv::Value::from_serde(&$capture) + }; +} + +/// Get a value from a type implementing `sval::Value`. +#[cfg(feature = "kv_unstable_sval")] +#[deprecated(note = "use the `key:sval = value` macro syntax instead")] +#[macro_export] +macro_rules! as_sval { + ($capture:expr) => { + $crate::kv::Value::from_sval(&$capture) + }; +} + +#[cfg(test)] +pub(crate) mod tests { + use super::*; + + impl<'v> Value<'v> { + pub(crate) fn to_token(&self) -> inner::Token { + self.inner.to_test_token() + } + } + + fn unsigned() -> impl Iterator<Item = Value<'static>> { + vec![ + Value::from(8u8), + Value::from(16u16), + Value::from(32u32), + Value::from(64u64), + Value::from(1usize), + Value::from(std::num::NonZeroU8::new(8).unwrap()), + Value::from(std::num::NonZeroU16::new(16).unwrap()), + Value::from(std::num::NonZeroU32::new(32).unwrap()), + Value::from(std::num::NonZeroU64::new(64).unwrap()), + Value::from(std::num::NonZeroUsize::new(1).unwrap()), + ] + .into_iter() + } + + fn signed() -> impl Iterator<Item = Value<'static>> { + vec![ + Value::from(-8i8), + Value::from(-16i16), + Value::from(-32i32), + Value::from(-64i64), + Value::from(-1isize), + Value::from(std::num::NonZeroI8::new(-8).unwrap()), + Value::from(std::num::NonZeroI16::new(-16).unwrap()), + Value::from(std::num::NonZeroI32::new(-32).unwrap()), + Value::from(std::num::NonZeroI64::new(-64).unwrap()), + Value::from(std::num::NonZeroIsize::new(-1).unwrap()), + ] + .into_iter() + } + + fn float() -> impl Iterator<Item = Value<'static>> { + vec![Value::from(32.32f32), Value::from(64.64f64)].into_iter() + } + + fn bool() -> impl Iterator<Item = Value<'static>> { + vec![Value::from(true), Value::from(false)].into_iter() + } + + fn str() -> impl Iterator<Item = Value<'static>> { + vec![Value::from("a string"), Value::from("a loong string")].into_iter() + } + + fn char() -> impl Iterator<Item = Value<'static>> { + vec![Value::from('a'), Value::from('â›°')].into_iter() + } + + #[test] + fn test_to_value_display() { + assert_eq!(42u64.to_value().to_string(), "42"); + assert_eq!(42i64.to_value().to_string(), "42"); + assert_eq!(42.01f64.to_value().to_string(), "42.01"); + assert_eq!(true.to_value().to_string(), "true"); + assert_eq!('a'.to_value().to_string(), "a"); + assert_eq!("a loong string".to_value().to_string(), "a loong string"); + assert_eq!(Some(true).to_value().to_string(), "true"); + assert_eq!(().to_value().to_string(), "None"); + assert_eq!(None::<bool>.to_value().to_string(), "None"); + } + + #[test] + fn test_to_value_structured() { + assert_eq!(42u64.to_value().to_token(), inner::Token::U64(42)); + assert_eq!(42i64.to_value().to_token(), inner::Token::I64(42)); + assert_eq!(42.01f64.to_value().to_token(), inner::Token::F64(42.01)); + assert_eq!(true.to_value().to_token(), inner::Token::Bool(true)); + assert_eq!('a'.to_value().to_token(), inner::Token::Char('a')); + assert_eq!( + "a loong string".to_value().to_token(), + inner::Token::Str("a loong string".into()) + ); + assert_eq!(Some(true).to_value().to_token(), inner::Token::Bool(true)); + assert_eq!(().to_value().to_token(), inner::Token::None); + assert_eq!(None::<bool>.to_value().to_token(), inner::Token::None); + } + + #[test] + fn test_to_number() { + for v in unsigned() { + assert!(v.to_u64().is_some()); + assert!(v.to_i64().is_some()); + } + + for v in signed() { + assert!(v.to_i64().is_some()); + } + + for v in unsigned().chain(signed()).chain(float()) { + assert!(v.to_f64().is_some()); + } + + for v in bool().chain(str()).chain(char()) { + assert!(v.to_u64().is_none()); + assert!(v.to_i64().is_none()); + assert!(v.to_f64().is_none()); + } + } + + #[test] + fn test_to_float() { + // Only integers from i32::MIN..=u32::MAX can be converted into floats + assert!(Value::from(i32::MIN).to_f64().is_some()); + assert!(Value::from(u32::MAX).to_f64().is_some()); + + assert!(Value::from((i32::MIN as i64) - 1).to_f64().is_none()); + assert!(Value::from((u32::MAX as u64) + 1).to_f64().is_none()); + } + + #[test] + fn test_to_cow_str() { + for v in str() { + assert!(v.to_borrowed_str().is_some()); + + #[cfg(feature = "kv_std")] + assert!(v.to_cow_str().is_some()); + } + + let short_lived = String::from("short lived"); + let v = Value::from(&*short_lived); + + assert!(v.to_borrowed_str().is_some()); + + #[cfg(feature = "kv_std")] + assert!(v.to_cow_str().is_some()); + + for v in unsigned().chain(signed()).chain(float()).chain(bool()) { + assert!(v.to_borrowed_str().is_none()); + + #[cfg(feature = "kv_std")] + assert!(v.to_cow_str().is_none()); + } + } + + #[test] + fn test_to_bool() { + for v in bool() { + assert!(v.to_bool().is_some()); + } + + for v in unsigned() + .chain(signed()) + .chain(float()) + .chain(str()) + .chain(char()) + { + assert!(v.to_bool().is_none()); + } + } + + #[test] + fn test_to_char() { + for v in char() { + assert!(v.to_char().is_some()); + } + + for v in unsigned() + .chain(signed()) + .chain(float()) + .chain(str()) + .chain(bool()) + { + assert!(v.to_char().is_none()); + } + } + + #[test] + fn test_visit_integer() { + struct Extract(Option<u64>); + + impl<'v> VisitValue<'v> for Extract { + fn visit_any(&mut self, value: Value) -> Result<(), Error> { + unimplemented!("unexpected value: {value:?}") + } + + fn visit_u64(&mut self, value: u64) -> Result<(), Error> { + self.0 = Some(value); + + Ok(()) + } + } + + let mut extract = Extract(None); + Value::from(42u64).visit(&mut extract).unwrap(); + + assert_eq!(Some(42), extract.0); + } + + #[test] + fn test_visit_borrowed_str() { + struct Extract<'v>(Option<&'v str>); + + impl<'v> VisitValue<'v> for Extract<'v> { + fn visit_any(&mut self, value: Value) -> Result<(), Error> { + unimplemented!("unexpected value: {value:?}") + } + + fn visit_borrowed_str(&mut self, value: &'v str) -> Result<(), Error> { + self.0 = Some(value); + + Ok(()) + } + } + + let mut extract = Extract(None); + + let short_lived = String::from("A short-lived string"); + Value::from(&*short_lived).visit(&mut extract).unwrap(); + + assert_eq!(Some("A short-lived string"), extract.0); + } +} diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/log/src/lib.rs b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/log/src/lib.rs new file mode 100644 index 0000000..6b43a9a --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/log/src/lib.rs @@ -0,0 +1,1878 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! A lightweight logging facade. +//! +//! The `log` crate provides a single logging API that abstracts over the +//! actual logging implementation. Libraries can use the logging API provided +//! by this crate, and the consumer of those libraries can choose the logging +//! implementation that is most suitable for its use case. +//! +//! If no logging implementation is selected, the facade falls back to a "noop" +//! implementation that ignores all log messages. The overhead in this case +//! is very small - just an integer load, comparison and jump. +//! +//! A log request consists of a _target_, a _level_, and a _body_. A target is a +//! string which defaults to the module path of the location of the log request, +//! though that default may be overridden. Logger implementations typically use +//! the target to filter requests based on some user configuration. +//! +//! # Usage +//! +//! The basic use of the log crate is through the five logging macros: [`error!`], +//! [`warn!`], [`info!`], [`debug!`] and [`trace!`] +//! where `error!` represents the highest-priority log messages +//! and `trace!` the lowest. The log messages are filtered by configuring +//! the log level to exclude messages with a lower priority. +//! Each of these macros accept format strings similarly to [`println!`]. +//! +//! +//! [`error!`]: ./macro.error.html +//! [`warn!`]: ./macro.warn.html +//! [`info!`]: ./macro.info.html +//! [`debug!`]: ./macro.debug.html +//! [`trace!`]: ./macro.trace.html +//! [`println!`]: https://doc.rust-lang.org/stable/std/macro.println.html +//! +//! Avoid writing expressions with side-effects in log statements. They may not be evaluated. +//! +//! ## In libraries +//! +//! Libraries should link only to the `log` crate, and use the provided +//! macros to log whatever information will be useful to downstream consumers. +//! +//! ### Examples +//! +//! ``` +//! # #[derive(Debug)] pub struct Yak(String); +//! # impl Yak { fn shave(&mut self, _: u32) {} } +//! # fn find_a_razor() -> Result<u32, u32> { Ok(1) } +//! use log::{info, warn}; +//! +//! pub fn shave_the_yak(yak: &mut Yak) { +//! info!(target: "yak_events", "Commencing yak shaving for {yak:?}"); +//! +//! loop { +//! match find_a_razor() { +//! Ok(razor) => { +//! info!("Razor located: {razor}"); +//! yak.shave(razor); +//! break; +//! } +//! Err(err) => { +//! warn!("Unable to locate a razor: {err}, retrying"); +//! } +//! } +//! } +//! } +//! # fn main() {} +//! ``` +//! +//! ## In executables +//! +//! Executables should choose a logging implementation and initialize it early in the +//! runtime of the program. Logging implementations will typically include a +//! function to do this. Any log messages generated before +//! the implementation is initialized will be ignored. +//! +//! The executable itself may use the `log` crate to log as well. +//! +//! ### Warning +//! +//! The logging system may only be initialized once. +//! +//! ## Structured logging +//! +//! If you enable the `kv` feature you can associate structured values +//! with your log records. If we take the example from before, we can include +//! some additional context besides what's in the formatted message: +//! +//! ``` +//! # use serde::Serialize; +//! # #[derive(Debug, Serialize)] pub struct Yak(String); +//! # impl Yak { fn shave(&mut self, _: u32) {} } +//! # fn find_a_razor() -> Result<u32, std::io::Error> { Ok(1) } +//! # #[cfg(feature = "kv_serde")] +//! # fn main() { +//! use log::{info, warn}; +//! +//! pub fn shave_the_yak(yak: &mut Yak) { +//! info!(target: "yak_events", yak:serde; "Commencing yak shaving"); +//! +//! loop { +//! match find_a_razor() { +//! Ok(razor) => { +//! info!(razor; "Razor located"); +//! yak.shave(razor); +//! break; +//! } +//! Err(e) => { +//! warn!(e:err; "Unable to locate a razor, retrying"); +//! } +//! } +//! } +//! } +//! # } +//! # #[cfg(not(feature = "kv_serde"))] +//! # fn main() {} +//! ``` +//! +//! See the [`kv`] module documentation for more details. +//! +//! # Available logging implementations +//! +//! In order to produce log output executables have to use +//! a logger implementation compatible with the facade. +//! There are many available implementations to choose from, +//! here are some of the most popular ones: +//! +//! * Simple minimal loggers: +//! * [env_logger] +//! * [colog] +//! * [simple_logger] +//! * [simplelog] +//! * [pretty_env_logger] +//! * [stderrlog] +//! * [flexi_logger] +//! * [call_logger] +//! * [structured-logger] +//! * Complex configurable frameworks: +//! * [log4rs] +//! * [fern] +//! * Adaptors for other facilities: +//! * [syslog] +//! * [slog-stdlog] +//! * [systemd-journal-logger] +//! * [android_log] +//! * [win_dbg_logger] +//! * [db_logger] +//! * [log-to-defmt] +//! * [logcontrol-log] +//! * For WebAssembly binaries: +//! * [console_log] +//! * For dynamic libraries: +//! * You may need to construct an FFI-safe wrapper over `log` to initialize in your libraries +//! * Utilities: +//! * [log_err] +//! * [log-reload] +//! +//! # Implementing a Logger +//! +//! Loggers implement the [`Log`] trait. Here's a very basic example that simply +//! logs all messages at the [`Error`][level_link], [`Warn`][level_link] or +//! [`Info`][level_link] levels to stdout: +//! +//! ``` +//! use log::{Record, Level, Metadata}; +//! +//! struct SimpleLogger; +//! +//! impl log::Log for SimpleLogger { +//! fn enabled(&self, metadata: &Metadata) -> bool { +//! metadata.level() <= Level::Info +//! } +//! +//! fn log(&self, record: &Record) { +//! if self.enabled(record.metadata()) { +//! println!("{} - {}", record.level(), record.args()); +//! } +//! } +//! +//! fn flush(&self) {} +//! } +//! +//! # fn main() {} +//! ``` +//! +//! Loggers are installed by calling the [`set_logger`] function. The maximum +//! log level also needs to be adjusted via the [`set_max_level`] function. The +//! logging facade uses this as an optimization to improve performance of log +//! messages at levels that are disabled. It's important to set it, as it +//! defaults to [`Off`][filter_link], so no log messages will ever be captured! +//! In the case of our example logger, we'll want to set the maximum log level +//! to [`Info`][filter_link], since we ignore any [`Debug`][level_link] or +//! [`Trace`][level_link] level log messages. A logging implementation should +//! provide a function that wraps a call to [`set_logger`] and +//! [`set_max_level`], handling initialization of the logger: +//! +//! ``` +//! # use log::{Level, Metadata}; +//! # struct SimpleLogger; +//! # impl log::Log for SimpleLogger { +//! # fn enabled(&self, _: &Metadata) -> bool { false } +//! # fn log(&self, _: &log::Record) {} +//! # fn flush(&self) {} +//! # } +//! # fn main() {} +//! use log::{SetLoggerError, LevelFilter}; +//! +//! static LOGGER: SimpleLogger = SimpleLogger; +//! +//! pub fn init() -> Result<(), SetLoggerError> { +//! log::set_logger(&LOGGER) +//! .map(|()| log::set_max_level(LevelFilter::Info)) +//! } +//! ``` +//! +//! Implementations that adjust their configurations at runtime should take care +//! to adjust the maximum log level as well. +//! +//! # Use with `std` +//! +//! `set_logger` requires you to provide a `&'static Log`, which can be hard to +//! obtain if your logger depends on some runtime configuration. The +//! `set_boxed_logger` function is available with the `std` Cargo feature. It is +//! identical to `set_logger` except that it takes a `Box<Log>` rather than a +//! `&'static Log`: +//! +//! ``` +//! # use log::{Level, LevelFilter, Log, SetLoggerError, Metadata}; +//! # struct SimpleLogger; +//! # impl log::Log for SimpleLogger { +//! # fn enabled(&self, _: &Metadata) -> bool { false } +//! # fn log(&self, _: &log::Record) {} +//! # fn flush(&self) {} +//! # } +//! # fn main() {} +//! # #[cfg(feature = "std")] +//! pub fn init() -> Result<(), SetLoggerError> { +//! log::set_boxed_logger(Box::new(SimpleLogger)) +//! .map(|()| log::set_max_level(LevelFilter::Info)) +//! } +//! ``` +//! +//! # Compile time filters +//! +//! Log levels can be statically disabled at compile time by enabling one of these Cargo features: +//! +//! * `max_level_off` +//! * `max_level_error` +//! * `max_level_warn` +//! * `max_level_info` +//! * `max_level_debug` +//! * `max_level_trace` +//! +//! Log invocations at disabled levels will be skipped and will not even be present in the +//! resulting binary. These features control the value of the `STATIC_MAX_LEVEL` constant. The +//! logging macros check this value before logging a message. By default, no levels are disabled. +//! +//! It is possible to override this level for release builds only with the following features: +//! +//! * `release_max_level_off` +//! * `release_max_level_error` +//! * `release_max_level_warn` +//! * `release_max_level_info` +//! * `release_max_level_debug` +//! * `release_max_level_trace` +//! +//! Libraries should avoid using the max level features because they're global and can't be changed +//! once they're set. +//! +//! For example, a crate can disable trace level logs in debug builds and trace, debug, and info +//! level logs in release builds with the following configuration: +//! +//! ```toml +//! [dependencies] +//! log = { version = "0.4", features = ["max_level_debug", "release_max_level_warn"] } +//! ``` +//! # Crate Feature Flags +//! +//! The following crate feature flags are available in addition to the filters. They are +//! configured in your `Cargo.toml`. +//! +//! * `std` allows use of `std` crate instead of the default `core`. Enables using `std::error` and +//! `set_boxed_logger` functionality. +//! * `serde` enables support for serialization and deserialization of `Level` and `LevelFilter`. +//! +//! ```toml +//! [dependencies] +//! log = { version = "0.4", features = ["std", "serde"] } +//! ``` +//! +//! # Version compatibility +//! +//! The 0.3 and 0.4 versions of the `log` crate are almost entirely compatible. Log messages +//! made using `log` 0.3 will forward transparently to a logger implementation using `log` 0.4. Log +//! messages made using `log` 0.4 will forward to a logger implementation using `log` 0.3, but the +//! module path and file name information associated with the message will unfortunately be lost. +//! +//! [`Log`]: trait.Log.html +//! [level_link]: enum.Level.html +//! [filter_link]: enum.LevelFilter.html +//! [`set_logger`]: fn.set_logger.html +//! [`set_max_level`]: fn.set_max_level.html +//! [`try_set_logger_raw`]: fn.try_set_logger_raw.html +//! [`shutdown_logger_raw`]: fn.shutdown_logger_raw.html +//! [env_logger]: https://docs.rs/env_logger/*/env_logger/ +//! [colog]: https://docs.rs/colog/*/colog/ +//! [simple_logger]: https://github.com/borntyping/rust-simple_logger +//! [simplelog]: https://github.com/drakulix/simplelog.rs +//! [pretty_env_logger]: https://docs.rs/pretty_env_logger/*/pretty_env_logger/ +//! [stderrlog]: https://docs.rs/stderrlog/*/stderrlog/ +//! [flexi_logger]: https://docs.rs/flexi_logger/*/flexi_logger/ +//! [call_logger]: https://docs.rs/call_logger/*/call_logger/ +//! [syslog]: https://docs.rs/syslog/*/syslog/ +//! [slog-stdlog]: https://docs.rs/slog-stdlog/*/slog_stdlog/ +//! [log4rs]: https://docs.rs/log4rs/*/log4rs/ +//! [fern]: https://docs.rs/fern/*/fern/ +//! [systemd-journal-logger]: https://docs.rs/systemd-journal-logger/*/systemd_journal_logger/ +//! [android_log]: https://docs.rs/android_log/*/android_log/ +//! [win_dbg_logger]: https://docs.rs/win_dbg_logger/*/win_dbg_logger/ +//! [db_logger]: https://docs.rs/db_logger/*/db_logger/ +//! [log-to-defmt]: https://docs.rs/log-to-defmt/*/log_to_defmt/ +//! [console_log]: https://docs.rs/console_log/*/console_log/ +//! [structured-logger]: https://docs.rs/structured-logger/latest/structured_logger/ +//! [logcontrol-log]: https://docs.rs/logcontrol-log/*/logcontrol_log/ +//! [log_err]: https://docs.rs/log_err/*/log_err/ +//! [log-reload]: https://docs.rs/log-reload/*/log_reload/ + +#![doc( + html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", + html_favicon_url = "https://www.rust-lang.org/favicon.ico", + html_root_url = "https://docs.rs/log/0.4.22" +)] +#![warn(missing_docs)] +#![deny(missing_debug_implementations, unconditional_recursion)] +#![cfg_attr(all(not(feature = "std"), not(test)), no_std)] + +#[cfg(any( + all(feature = "max_level_off", feature = "max_level_error"), + all(feature = "max_level_off", feature = "max_level_warn"), + all(feature = "max_level_off", feature = "max_level_info"), + all(feature = "max_level_off", feature = "max_level_debug"), + all(feature = "max_level_off", feature = "max_level_trace"), + all(feature = "max_level_error", feature = "max_level_warn"), + all(feature = "max_level_error", feature = "max_level_info"), + all(feature = "max_level_error", feature = "max_level_debug"), + all(feature = "max_level_error", feature = "max_level_trace"), + all(feature = "max_level_warn", feature = "max_level_info"), + all(feature = "max_level_warn", feature = "max_level_debug"), + all(feature = "max_level_warn", feature = "max_level_trace"), + all(feature = "max_level_info", feature = "max_level_debug"), + all(feature = "max_level_info", feature = "max_level_trace"), + all(feature = "max_level_debug", feature = "max_level_trace"), +))] +compile_error!("multiple max_level_* features set"); + +#[rustfmt::skip] +#[cfg(any( + all(feature = "release_max_level_off", feature = "release_max_level_error"), + all(feature = "release_max_level_off", feature = "release_max_level_warn"), + all(feature = "release_max_level_off", feature = "release_max_level_info"), + all(feature = "release_max_level_off", feature = "release_max_level_debug"), + all(feature = "release_max_level_off", feature = "release_max_level_trace"), + all(feature = "release_max_level_error", feature = "release_max_level_warn"), + all(feature = "release_max_level_error", feature = "release_max_level_info"), + all(feature = "release_max_level_error", feature = "release_max_level_debug"), + all(feature = "release_max_level_error", feature = "release_max_level_trace"), + all(feature = "release_max_level_warn", feature = "release_max_level_info"), + all(feature = "release_max_level_warn", feature = "release_max_level_debug"), + all(feature = "release_max_level_warn", feature = "release_max_level_trace"), + all(feature = "release_max_level_info", feature = "release_max_level_debug"), + all(feature = "release_max_level_info", feature = "release_max_level_trace"), + all(feature = "release_max_level_debug", feature = "release_max_level_trace"), +))] +compile_error!("multiple release_max_level_* features set"); + +#[cfg(all(not(feature = "std"), not(test)))] +extern crate core as std; + +use std::cfg; +#[cfg(feature = "std")] +use std::error; +use std::str::FromStr; +use std::{cmp, fmt, mem}; + +#[macro_use] +mod macros; +mod serde; + +#[cfg(feature = "kv")] +pub mod kv; + +#[cfg(target_has_atomic = "ptr")] +use std::sync::atomic::{AtomicUsize, Ordering}; + +#[cfg(not(target_has_atomic = "ptr"))] +use std::cell::Cell; +#[cfg(not(target_has_atomic = "ptr"))] +use std::sync::atomic::Ordering; + +#[cfg(not(target_has_atomic = "ptr"))] +struct AtomicUsize { + v: Cell<usize>, +} + +#[cfg(not(target_has_atomic = "ptr"))] +impl AtomicUsize { + const fn new(v: usize) -> AtomicUsize { + AtomicUsize { v: Cell::new(v) } + } + + fn load(&self, _order: Ordering) -> usize { + self.v.get() + } + + fn store(&self, val: usize, _order: Ordering) { + self.v.set(val) + } + + #[cfg(target_has_atomic = "ptr")] + fn compare_exchange( + &self, + current: usize, + new: usize, + _success: Ordering, + _failure: Ordering, + ) -> Result<usize, usize> { + let prev = self.v.get(); + if current == prev { + self.v.set(new); + } + Ok(prev) + } +} + +// Any platform without atomics is unlikely to have multiple cores, so +// writing via Cell will not be a race condition. +#[cfg(not(target_has_atomic = "ptr"))] +unsafe impl Sync for AtomicUsize {} + +// The LOGGER static holds a pointer to the global logger. It is protected by +// the STATE static which determines whether LOGGER has been initialized yet. +static mut LOGGER: &dyn Log = &NopLogger; + +static STATE: AtomicUsize = AtomicUsize::new(0); + +// There are three different states that we care about: the logger's +// uninitialized, the logger's initializing (set_logger's been called but +// LOGGER hasn't actually been set yet), or the logger's active. +const UNINITIALIZED: usize = 0; +const INITIALIZING: usize = 1; +const INITIALIZED: usize = 2; + +static MAX_LOG_LEVEL_FILTER: AtomicUsize = AtomicUsize::new(0); + +static LOG_LEVEL_NAMES: [&str; 6] = ["OFF", "ERROR", "WARN", "INFO", "DEBUG", "TRACE"]; + +static SET_LOGGER_ERROR: &str = "attempted to set a logger after the logging system \ + was already initialized"; +static LEVEL_PARSE_ERROR: &str = + "attempted to convert a string that doesn't match an existing log level"; + +/// An enum representing the available verbosity levels of the logger. +/// +/// Typical usage includes: checking if a certain `Level` is enabled with +/// [`log_enabled!`](macro.log_enabled.html), specifying the `Level` of +/// [`log!`](macro.log.html), and comparing a `Level` directly to a +/// [`LevelFilter`](enum.LevelFilter.html). +#[repr(usize)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] +pub enum Level { + /// The "error" level. + /// + /// Designates very serious errors. + // This way these line up with the discriminants for LevelFilter below + // This works because Rust treats field-less enums the same way as C does: + // https://doc.rust-lang.org/reference/items/enumerations.html#custom-discriminant-values-for-field-less-enumerations + Error = 1, + /// The "warn" level. + /// + /// Designates hazardous situations. + Warn, + /// The "info" level. + /// + /// Designates useful information. + Info, + /// The "debug" level. + /// + /// Designates lower priority information. + Debug, + /// The "trace" level. + /// + /// Designates very low priority, often extremely verbose, information. + Trace, +} + +impl PartialEq<LevelFilter> for Level { + #[inline] + fn eq(&self, other: &LevelFilter) -> bool { + *self as usize == *other as usize + } +} + +impl PartialOrd<LevelFilter> for Level { + #[inline] + fn partial_cmp(&self, other: &LevelFilter) -> Option<cmp::Ordering> { + Some((*self as usize).cmp(&(*other as usize))) + } +} + +impl FromStr for Level { + type Err = ParseLevelError; + fn from_str(level: &str) -> Result<Level, Self::Err> { + LOG_LEVEL_NAMES + .iter() + .position(|&name| name.eq_ignore_ascii_case(level)) + .into_iter() + .filter(|&idx| idx != 0) + .map(|idx| Level::from_usize(idx).unwrap()) + .next() + .ok_or(ParseLevelError(())) + } +} + +impl fmt::Display for Level { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.pad(self.as_str()) + } +} + +impl Level { + fn from_usize(u: usize) -> Option<Level> { + match u { + 1 => Some(Level::Error), + 2 => Some(Level::Warn), + 3 => Some(Level::Info), + 4 => Some(Level::Debug), + 5 => Some(Level::Trace), + _ => None, + } + } + + /// Returns the most verbose logging level. + #[inline] + pub fn max() -> Level { + Level::Trace + } + + /// Converts the `Level` to the equivalent `LevelFilter`. + #[inline] + pub fn to_level_filter(&self) -> LevelFilter { + LevelFilter::from_usize(*self as usize).unwrap() + } + + /// Returns the string representation of the `Level`. + /// + /// This returns the same string as the `fmt::Display` implementation. + pub fn as_str(&self) -> &'static str { + LOG_LEVEL_NAMES[*self as usize] + } + + /// Iterate through all supported logging levels. + /// + /// The order of iteration is from more severe to less severe log messages. + /// + /// # Examples + /// + /// ``` + /// use log::Level; + /// + /// let mut levels = Level::iter(); + /// + /// assert_eq!(Some(Level::Error), levels.next()); + /// assert_eq!(Some(Level::Trace), levels.last()); + /// ``` + pub fn iter() -> impl Iterator<Item = Self> { + (1..6).map(|i| Self::from_usize(i).unwrap()) + } +} + +/// An enum representing the available verbosity level filters of the logger. +/// +/// A `LevelFilter` may be compared directly to a [`Level`]. Use this type +/// to get and set the maximum log level with [`max_level()`] and [`set_max_level`]. +/// +/// [`Level`]: enum.Level.html +/// [`max_level()`]: fn.max_level.html +/// [`set_max_level`]: fn.set_max_level.html +#[repr(usize)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] +pub enum LevelFilter { + /// A level lower than all log levels. + Off, + /// Corresponds to the `Error` log level. + Error, + /// Corresponds to the `Warn` log level. + Warn, + /// Corresponds to the `Info` log level. + Info, + /// Corresponds to the `Debug` log level. + Debug, + /// Corresponds to the `Trace` log level. + Trace, +} + +impl PartialEq<Level> for LevelFilter { + #[inline] + fn eq(&self, other: &Level) -> bool { + other.eq(self) + } +} + +impl PartialOrd<Level> for LevelFilter { + #[inline] + fn partial_cmp(&self, other: &Level) -> Option<cmp::Ordering> { + Some((*self as usize).cmp(&(*other as usize))) + } +} + +impl FromStr for LevelFilter { + type Err = ParseLevelError; + fn from_str(level: &str) -> Result<LevelFilter, Self::Err> { + LOG_LEVEL_NAMES + .iter() + .position(|&name| name.eq_ignore_ascii_case(level)) + .map(|p| LevelFilter::from_usize(p).unwrap()) + .ok_or(ParseLevelError(())) + } +} + +impl fmt::Display for LevelFilter { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.pad(self.as_str()) + } +} + +impl LevelFilter { + fn from_usize(u: usize) -> Option<LevelFilter> { + match u { + 0 => Some(LevelFilter::Off), + 1 => Some(LevelFilter::Error), + 2 => Some(LevelFilter::Warn), + 3 => Some(LevelFilter::Info), + 4 => Some(LevelFilter::Debug), + 5 => Some(LevelFilter::Trace), + _ => None, + } + } + + /// Returns the most verbose logging level filter. + #[inline] + pub fn max() -> LevelFilter { + LevelFilter::Trace + } + + /// Converts `self` to the equivalent `Level`. + /// + /// Returns `None` if `self` is `LevelFilter::Off`. + #[inline] + pub fn to_level(&self) -> Option<Level> { + Level::from_usize(*self as usize) + } + + /// Returns the string representation of the `LevelFilter`. + /// + /// This returns the same string as the `fmt::Display` implementation. + pub fn as_str(&self) -> &'static str { + LOG_LEVEL_NAMES[*self as usize] + } + + /// Iterate through all supported filtering levels. + /// + /// The order of iteration is from less to more verbose filtering. + /// + /// # Examples + /// + /// ``` + /// use log::LevelFilter; + /// + /// let mut levels = LevelFilter::iter(); + /// + /// assert_eq!(Some(LevelFilter::Off), levels.next()); + /// assert_eq!(Some(LevelFilter::Trace), levels.last()); + /// ``` + pub fn iter() -> impl Iterator<Item = Self> { + (0..6).map(|i| Self::from_usize(i).unwrap()) + } +} + +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] +enum MaybeStaticStr<'a> { + Static(&'static str), + Borrowed(&'a str), +} + +impl<'a> MaybeStaticStr<'a> { + #[inline] + fn get(&self) -> &'a str { + match *self { + MaybeStaticStr::Static(s) => s, + MaybeStaticStr::Borrowed(s) => s, + } + } +} + +/// The "payload" of a log message. +/// +/// # Use +/// +/// `Record` structures are passed as parameters to the [`log`][method.log] +/// method of the [`Log`] trait. Logger implementors manipulate these +/// structures in order to display log messages. `Record`s are automatically +/// created by the [`log!`] macro and so are not seen by log users. +/// +/// Note that the [`level()`] and [`target()`] accessors are equivalent to +/// `self.metadata().level()` and `self.metadata().target()` respectively. +/// These methods are provided as a convenience for users of this structure. +/// +/// # Example +/// +/// The following example shows a simple logger that displays the level, +/// module path, and message of any `Record` that is passed to it. +/// +/// ``` +/// struct SimpleLogger; +/// +/// impl log::Log for SimpleLogger { +/// fn enabled(&self, _metadata: &log::Metadata) -> bool { +/// true +/// } +/// +/// fn log(&self, record: &log::Record) { +/// if !self.enabled(record.metadata()) { +/// return; +/// } +/// +/// println!("{}:{} -- {}", +/// record.level(), +/// record.target(), +/// record.args()); +/// } +/// fn flush(&self) {} +/// } +/// ``` +/// +/// [method.log]: trait.Log.html#tymethod.log +/// [`Log`]: trait.Log.html +/// [`log!`]: macro.log.html +/// [`level()`]: struct.Record.html#method.level +/// [`target()`]: struct.Record.html#method.target +#[derive(Clone, Debug)] +pub struct Record<'a> { + metadata: Metadata<'a>, + args: fmt::Arguments<'a>, + module_path: Option<MaybeStaticStr<'a>>, + file: Option<MaybeStaticStr<'a>>, + line: Option<u32>, + #[cfg(feature = "kv")] + key_values: KeyValues<'a>, +} + +// This wrapper type is only needed so we can +// `#[derive(Debug)]` on `Record`. It also +// provides a useful `Debug` implementation for +// the underlying `Source`. +#[cfg(feature = "kv")] +#[derive(Clone)] +struct KeyValues<'a>(&'a dyn kv::Source); + +#[cfg(feature = "kv")] +impl<'a> fmt::Debug for KeyValues<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut visitor = f.debug_map(); + self.0.visit(&mut visitor).map_err(|_| fmt::Error)?; + visitor.finish() + } +} + +impl<'a> Record<'a> { + /// Returns a new builder. + #[inline] + pub fn builder() -> RecordBuilder<'a> { + RecordBuilder::new() + } + + /// The message body. + #[inline] + pub fn args(&self) -> &fmt::Arguments<'a> { + &self.args + } + + /// Metadata about the log directive. + #[inline] + pub fn metadata(&self) -> &Metadata<'a> { + &self.metadata + } + + /// The verbosity level of the message. + #[inline] + pub fn level(&self) -> Level { + self.metadata.level() + } + + /// The name of the target of the directive. + #[inline] + pub fn target(&self) -> &'a str { + self.metadata.target() + } + + /// The module path of the message. + #[inline] + pub fn module_path(&self) -> Option<&'a str> { + self.module_path.map(|s| s.get()) + } + + /// The module path of the message, if it is a `'static` string. + #[inline] + pub fn module_path_static(&self) -> Option<&'static str> { + match self.module_path { + Some(MaybeStaticStr::Static(s)) => Some(s), + _ => None, + } + } + + /// The source file containing the message. + #[inline] + pub fn file(&self) -> Option<&'a str> { + self.file.map(|s| s.get()) + } + + /// The source file containing the message, if it is a `'static` string. + #[inline] + pub fn file_static(&self) -> Option<&'static str> { + match self.file { + Some(MaybeStaticStr::Static(s)) => Some(s), + _ => None, + } + } + + /// The line containing the message. + #[inline] + pub fn line(&self) -> Option<u32> { + self.line + } + + /// The structured key-value pairs associated with the message. + #[cfg(feature = "kv")] + #[inline] + pub fn key_values(&self) -> &dyn kv::Source { + self.key_values.0 + } + + /// Create a new [`RecordBuilder`](struct.RecordBuilder.html) based on this record. + #[cfg(feature = "kv")] + #[inline] + pub fn to_builder(&self) -> RecordBuilder { + RecordBuilder { + record: Record { + metadata: Metadata { + level: self.metadata.level, + target: self.metadata.target, + }, + args: self.args, + module_path: self.module_path, + file: self.file, + line: self.line, + key_values: self.key_values.clone(), + }, + } + } +} + +/// Builder for [`Record`](struct.Record.html). +/// +/// Typically should only be used by log library creators or for testing and "shim loggers". +/// The `RecordBuilder` can set the different parameters of `Record` object, and returns +/// the created object when `build` is called. +/// +/// # Examples +/// +/// ``` +/// use log::{Level, Record}; +/// +/// let record = Record::builder() +/// .args(format_args!("Error!")) +/// .level(Level::Error) +/// .target("myApp") +/// .file(Some("server.rs")) +/// .line(Some(144)) +/// .module_path(Some("server")) +/// .build(); +/// ``` +/// +/// Alternatively, use [`MetadataBuilder`](struct.MetadataBuilder.html): +/// +/// ``` +/// use log::{Record, Level, MetadataBuilder}; +/// +/// let error_metadata = MetadataBuilder::new() +/// .target("myApp") +/// .level(Level::Error) +/// .build(); +/// +/// let record = Record::builder() +/// .metadata(error_metadata) +/// .args(format_args!("Error!")) +/// .line(Some(433)) +/// .file(Some("app.rs")) +/// .module_path(Some("server")) +/// .build(); +/// ``` +#[derive(Debug)] +pub struct RecordBuilder<'a> { + record: Record<'a>, +} + +impl<'a> RecordBuilder<'a> { + /// Construct new `RecordBuilder`. + /// + /// The default options are: + /// + /// - `args`: [`format_args!("")`] + /// - `metadata`: [`Metadata::builder().build()`] + /// - `module_path`: `None` + /// - `file`: `None` + /// - `line`: `None` + /// + /// [`format_args!("")`]: https://doc.rust-lang.org/std/macro.format_args.html + /// [`Metadata::builder().build()`]: struct.MetadataBuilder.html#method.build + #[inline] + pub fn new() -> RecordBuilder<'a> { + RecordBuilder { + record: Record { + args: format_args!(""), + metadata: Metadata::builder().build(), + module_path: None, + file: None, + line: None, + #[cfg(feature = "kv")] + key_values: KeyValues(&None::<(kv::Key, kv::Value)>), + }, + } + } + + /// Set [`args`](struct.Record.html#method.args). + #[inline] + pub fn args(&mut self, args: fmt::Arguments<'a>) -> &mut RecordBuilder<'a> { + self.record.args = args; + self + } + + /// Set [`metadata`](struct.Record.html#method.metadata). Construct a `Metadata` object with [`MetadataBuilder`](struct.MetadataBuilder.html). + #[inline] + pub fn metadata(&mut self, metadata: Metadata<'a>) -> &mut RecordBuilder<'a> { + self.record.metadata = metadata; + self + } + + /// Set [`Metadata::level`](struct.Metadata.html#method.level). + #[inline] + pub fn level(&mut self, level: Level) -> &mut RecordBuilder<'a> { + self.record.metadata.level = level; + self + } + + /// Set [`Metadata::target`](struct.Metadata.html#method.target) + #[inline] + pub fn target(&mut self, target: &'a str) -> &mut RecordBuilder<'a> { + self.record.metadata.target = target; + self + } + + /// Set [`module_path`](struct.Record.html#method.module_path) + #[inline] + pub fn module_path(&mut self, path: Option<&'a str>) -> &mut RecordBuilder<'a> { + self.record.module_path = path.map(MaybeStaticStr::Borrowed); + self + } + + /// Set [`module_path`](struct.Record.html#method.module_path) to a `'static` string + #[inline] + pub fn module_path_static(&mut self, path: Option<&'static str>) -> &mut RecordBuilder<'a> { + self.record.module_path = path.map(MaybeStaticStr::Static); + self + } + + /// Set [`file`](struct.Record.html#method.file) + #[inline] + pub fn file(&mut self, file: Option<&'a str>) -> &mut RecordBuilder<'a> { + self.record.file = file.map(MaybeStaticStr::Borrowed); + self + } + + /// Set [`file`](struct.Record.html#method.file) to a `'static` string. + #[inline] + pub fn file_static(&mut self, file: Option<&'static str>) -> &mut RecordBuilder<'a> { + self.record.file = file.map(MaybeStaticStr::Static); + self + } + + /// Set [`line`](struct.Record.html#method.line) + #[inline] + pub fn line(&mut self, line: Option<u32>) -> &mut RecordBuilder<'a> { + self.record.line = line; + self + } + + /// Set [`key_values`](struct.Record.html#method.key_values) + #[cfg(feature = "kv")] + #[inline] + pub fn key_values(&mut self, kvs: &'a dyn kv::Source) -> &mut RecordBuilder<'a> { + self.record.key_values = KeyValues(kvs); + self + } + + /// Invoke the builder and return a `Record` + #[inline] + pub fn build(&self) -> Record<'a> { + self.record.clone() + } +} + +impl<'a> Default for RecordBuilder<'a> { + fn default() -> Self { + Self::new() + } +} + +/// Metadata about a log message. +/// +/// # Use +/// +/// `Metadata` structs are created when users of the library use +/// logging macros. +/// +/// They are consumed by implementations of the `Log` trait in the +/// `enabled` method. +/// +/// `Record`s use `Metadata` to determine the log message's severity +/// and target. +/// +/// Users should use the `log_enabled!` macro in their code to avoid +/// constructing expensive log messages. +/// +/// # Examples +/// +/// ``` +/// use log::{Record, Level, Metadata}; +/// +/// struct MyLogger; +/// +/// impl log::Log for MyLogger { +/// fn enabled(&self, metadata: &Metadata) -> bool { +/// metadata.level() <= Level::Info +/// } +/// +/// fn log(&self, record: &Record) { +/// if self.enabled(record.metadata()) { +/// println!("{} - {}", record.level(), record.args()); +/// } +/// } +/// fn flush(&self) {} +/// } +/// +/// # fn main(){} +/// ``` +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] +pub struct Metadata<'a> { + level: Level, + target: &'a str, +} + +impl<'a> Metadata<'a> { + /// Returns a new builder. + #[inline] + pub fn builder() -> MetadataBuilder<'a> { + MetadataBuilder::new() + } + + /// The verbosity level of the message. + #[inline] + pub fn level(&self) -> Level { + self.level + } + + /// The name of the target of the directive. + #[inline] + pub fn target(&self) -> &'a str { + self.target + } +} + +/// Builder for [`Metadata`](struct.Metadata.html). +/// +/// Typically should only be used by log library creators or for testing and "shim loggers". +/// The `MetadataBuilder` can set the different parameters of a `Metadata` object, and returns +/// the created object when `build` is called. +/// +/// # Example +/// +/// ``` +/// let target = "myApp"; +/// use log::{Level, MetadataBuilder}; +/// let metadata = MetadataBuilder::new() +/// .level(Level::Debug) +/// .target(target) +/// .build(); +/// ``` +#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] +pub struct MetadataBuilder<'a> { + metadata: Metadata<'a>, +} + +impl<'a> MetadataBuilder<'a> { + /// Construct a new `MetadataBuilder`. + /// + /// The default options are: + /// + /// - `level`: `Level::Info` + /// - `target`: `""` + #[inline] + pub fn new() -> MetadataBuilder<'a> { + MetadataBuilder { + metadata: Metadata { + level: Level::Info, + target: "", + }, + } + } + + /// Setter for [`level`](struct.Metadata.html#method.level). + #[inline] + pub fn level(&mut self, arg: Level) -> &mut MetadataBuilder<'a> { + self.metadata.level = arg; + self + } + + /// Setter for [`target`](struct.Metadata.html#method.target). + #[inline] + pub fn target(&mut self, target: &'a str) -> &mut MetadataBuilder<'a> { + self.metadata.target = target; + self + } + + /// Returns a `Metadata` object. + #[inline] + pub fn build(&self) -> Metadata<'a> { + self.metadata.clone() + } +} + +impl<'a> Default for MetadataBuilder<'a> { + fn default() -> Self { + Self::new() + } +} + +/// A trait encapsulating the operations required of a logger. +pub trait Log: Sync + Send { + /// Determines if a log message with the specified metadata would be + /// logged. + /// + /// This is used by the `log_enabled!` macro to allow callers to avoid + /// expensive computation of log message arguments if the message would be + /// discarded anyway. + /// + /// # For implementors + /// + /// This method isn't called automatically by the `log!` macros. + /// It's up to an implementation of the `Log` trait to call `enabled` in its own + /// `log` method implementation to guarantee that filtering is applied. + fn enabled(&self, metadata: &Metadata) -> bool; + + /// Logs the `Record`. + /// + /// # For implementors + /// + /// Note that `enabled` is *not* necessarily called before this method. + /// Implementations of `log` should perform all necessary filtering + /// internally. + fn log(&self, record: &Record); + + /// Flushes any buffered records. + /// + /// # For implementors + /// + /// This method isn't called automatically by the `log!` macros. + /// It can be called manually on shut-down to ensure any in-flight records are flushed. + fn flush(&self); +} + +// Just used as a dummy initial value for LOGGER +struct NopLogger; + +impl Log for NopLogger { + fn enabled(&self, _: &Metadata) -> bool { + false + } + + fn log(&self, _: &Record) {} + fn flush(&self) {} +} + +impl<T> Log for &'_ T +where + T: ?Sized + Log, +{ + fn enabled(&self, metadata: &Metadata) -> bool { + (**self).enabled(metadata) + } + + fn log(&self, record: &Record) { + (**self).log(record); + } + fn flush(&self) { + (**self).flush(); + } +} + +#[cfg(feature = "std")] +impl<T> Log for std::boxed::Box<T> +where + T: ?Sized + Log, +{ + fn enabled(&self, metadata: &Metadata) -> bool { + self.as_ref().enabled(metadata) + } + + fn log(&self, record: &Record) { + self.as_ref().log(record); + } + fn flush(&self) { + self.as_ref().flush(); + } +} + +#[cfg(feature = "std")] +impl<T> Log for std::sync::Arc<T> +where + T: ?Sized + Log, +{ + fn enabled(&self, metadata: &Metadata) -> bool { + self.as_ref().enabled(metadata) + } + + fn log(&self, record: &Record) { + self.as_ref().log(record); + } + fn flush(&self) { + self.as_ref().flush(); + } +} + +/// Sets the global maximum log level. +/// +/// Generally, this should only be called by the active logging implementation. +/// +/// Note that `Trace` is the maximum level, because it provides the maximum amount of detail in the emitted logs. +#[inline] +#[cfg(target_has_atomic = "ptr")] +pub fn set_max_level(level: LevelFilter) { + MAX_LOG_LEVEL_FILTER.store(level as usize, Ordering::Relaxed); +} + +/// A thread-unsafe version of [`set_max_level`]. +/// +/// This function is available on all platforms, even those that do not have +/// support for atomics that is needed by [`set_max_level`]. +/// +/// In almost all cases, [`set_max_level`] should be preferred. +/// +/// # Safety +/// +/// This function is only safe to call when it cannot race with any other +/// calls to `set_max_level` or `set_max_level_racy`. +/// +/// This can be upheld by (for example) making sure that **there are no other +/// threads**, and (on embedded) that **interrupts are disabled**. +/// +/// It is safe to use all other logging functions while this function runs +/// (including all logging macros). +/// +/// [`set_max_level`]: fn.set_max_level.html +#[inline] +pub unsafe fn set_max_level_racy(level: LevelFilter) { + // `MAX_LOG_LEVEL_FILTER` uses a `Cell` as the underlying primitive when a + // platform doesn't support `target_has_atomic = "ptr"`, so even though this looks the same + // as `set_max_level` it may have different safety properties. + MAX_LOG_LEVEL_FILTER.store(level as usize, Ordering::Relaxed); +} + +/// Returns the current maximum log level. +/// +/// The [`log!`], [`error!`], [`warn!`], [`info!`], [`debug!`], and [`trace!`] macros check +/// this value and discard any message logged at a higher level. The maximum +/// log level is set by the [`set_max_level`] function. +/// +/// [`log!`]: macro.log.html +/// [`error!`]: macro.error.html +/// [`warn!`]: macro.warn.html +/// [`info!`]: macro.info.html +/// [`debug!`]: macro.debug.html +/// [`trace!`]: macro.trace.html +/// [`set_max_level`]: fn.set_max_level.html +#[inline(always)] +pub fn max_level() -> LevelFilter { + // Since `LevelFilter` is `repr(usize)`, + // this transmute is sound if and only if `MAX_LOG_LEVEL_FILTER` + // is set to a usize that is a valid discriminant for `LevelFilter`. + // Since `MAX_LOG_LEVEL_FILTER` is private, the only time it's set + // is by `set_max_level` above, i.e. by casting a `LevelFilter` to `usize`. + // So any usize stored in `MAX_LOG_LEVEL_FILTER` is a valid discriminant. + unsafe { mem::transmute(MAX_LOG_LEVEL_FILTER.load(Ordering::Relaxed)) } +} + +/// Sets the global logger to a `Box<Log>`. +/// +/// This is a simple convenience wrapper over `set_logger`, which takes a +/// `Box<Log>` rather than a `&'static Log`. See the documentation for +/// [`set_logger`] for more details. +/// +/// Requires the `std` feature. +/// +/// # Errors +/// +/// An error is returned if a logger has already been set. +/// +/// [`set_logger`]: fn.set_logger.html +#[cfg(all(feature = "std", target_has_atomic = "ptr"))] +pub fn set_boxed_logger(logger: Box<dyn Log>) -> Result<(), SetLoggerError> { + set_logger_inner(|| Box::leak(logger)) +} + +/// Sets the global logger to a `&'static Log`. +/// +/// This function may only be called once in the lifetime of a program. Any log +/// events that occur before the call to `set_logger` completes will be ignored. +/// +/// This function does not typically need to be called manually. Logger +/// implementations should provide an initialization method that installs the +/// logger internally. +/// +/// # Availability +/// +/// This method is available even when the `std` feature is disabled. However, +/// it is currently unavailable on `thumbv6` targets, which lack support for +/// some atomic operations which are used by this function. Even on those +/// targets, [`set_logger_racy`] will be available. +/// +/// # Errors +/// +/// An error is returned if a logger has already been set. +/// +/// # Examples +/// +/// ``` +/// use log::{error, info, warn, Record, Level, Metadata, LevelFilter}; +/// +/// static MY_LOGGER: MyLogger = MyLogger; +/// +/// struct MyLogger; +/// +/// impl log::Log for MyLogger { +/// fn enabled(&self, metadata: &Metadata) -> bool { +/// metadata.level() <= Level::Info +/// } +/// +/// fn log(&self, record: &Record) { +/// if self.enabled(record.metadata()) { +/// println!("{} - {}", record.level(), record.args()); +/// } +/// } +/// fn flush(&self) {} +/// } +/// +/// # fn main(){ +/// log::set_logger(&MY_LOGGER).unwrap(); +/// log::set_max_level(LevelFilter::Info); +/// +/// info!("hello log"); +/// warn!("warning"); +/// error!("oops"); +/// # } +/// ``` +/// +/// [`set_logger_racy`]: fn.set_logger_racy.html +#[cfg(target_has_atomic = "ptr")] +pub fn set_logger(logger: &'static dyn Log) -> Result<(), SetLoggerError> { + set_logger_inner(|| logger) +} + +#[cfg(target_has_atomic = "ptr")] +fn set_logger_inner<F>(make_logger: F) -> Result<(), SetLoggerError> +where + F: FnOnce() -> &'static dyn Log, +{ + match STATE.compare_exchange( + UNINITIALIZED, + INITIALIZING, + Ordering::Acquire, + Ordering::Relaxed, + ) { + Ok(UNINITIALIZED) => { + unsafe { + LOGGER = make_logger(); + } + STATE.store(INITIALIZED, Ordering::Release); + Ok(()) + } + Err(INITIALIZING) => { + while STATE.load(Ordering::Relaxed) == INITIALIZING { + std::hint::spin_loop(); + } + Err(SetLoggerError(())) + } + _ => Err(SetLoggerError(())), + } +} + +/// A thread-unsafe version of [`set_logger`]. +/// +/// This function is available on all platforms, even those that do not have +/// support for atomics that is needed by [`set_logger`]. +/// +/// In almost all cases, [`set_logger`] should be preferred. +/// +/// # Safety +/// +/// This function is only safe to call when it cannot race with any other +/// calls to `set_logger` or `set_logger_racy`. +/// +/// This can be upheld by (for example) making sure that **there are no other +/// threads**, and (on embedded) that **interrupts are disabled**. +/// +/// It is safe to use other logging functions while this function runs +/// (including all logging macros). +/// +/// [`set_logger`]: fn.set_logger.html +pub unsafe fn set_logger_racy(logger: &'static dyn Log) -> Result<(), SetLoggerError> { + match STATE.load(Ordering::Acquire) { + UNINITIALIZED => { + LOGGER = logger; + STATE.store(INITIALIZED, Ordering::Release); + Ok(()) + } + INITIALIZING => { + // This is just plain UB, since we were racing another initialization function + unreachable!("set_logger_racy must not be used with other initialization functions") + } + _ => Err(SetLoggerError(())), + } +} + +/// The type returned by [`set_logger`] if [`set_logger`] has already been called. +/// +/// [`set_logger`]: fn.set_logger.html +#[allow(missing_copy_implementations)] +#[derive(Debug)] +pub struct SetLoggerError(()); + +impl fmt::Display for SetLoggerError { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str(SET_LOGGER_ERROR) + } +} + +// The Error trait is not available in libcore +#[cfg(feature = "std")] +impl error::Error for SetLoggerError {} + +/// The type returned by [`from_str`] when the string doesn't match any of the log levels. +/// +/// [`from_str`]: https://doc.rust-lang.org/std/str/trait.FromStr.html#tymethod.from_str +#[allow(missing_copy_implementations)] +#[derive(Debug, PartialEq, Eq)] +pub struct ParseLevelError(()); + +impl fmt::Display for ParseLevelError { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str(LEVEL_PARSE_ERROR) + } +} + +// The Error trait is not available in libcore +#[cfg(feature = "std")] +impl error::Error for ParseLevelError {} + +/// Returns a reference to the logger. +/// +/// If a logger has not been set, a no-op implementation is returned. +pub fn logger() -> &'static dyn Log { + // Acquire memory ordering guarantees that current thread would see any + // memory writes that happened before store of the value + // into `STATE` with memory ordering `Release` or stronger. + // + // Since the value `INITIALIZED` is written only after `LOGGER` was + // initialized, observing it after `Acquire` load here makes both + // write to the `LOGGER` static and initialization of the logger + // internal state synchronized with current thread. + if STATE.load(Ordering::Acquire) != INITIALIZED { + static NOP: NopLogger = NopLogger; + &NOP + } else { + unsafe { LOGGER } + } +} + +// WARNING: this is not part of the crate's public API and is subject to change at any time +#[doc(hidden)] +pub mod __private_api; + +/// The statically resolved maximum log level. +/// +/// See the crate level documentation for information on how to configure this. +/// +/// This value is checked by the log macros, but not by the `Log`ger returned by +/// the [`logger`] function. Code that manually calls functions on that value +/// should compare the level against this value. +/// +/// [`logger`]: fn.logger.html +pub const STATIC_MAX_LEVEL: LevelFilter = match cfg!(debug_assertions) { + false if cfg!(feature = "release_max_level_off") => LevelFilter::Off, + false if cfg!(feature = "release_max_level_error") => LevelFilter::Error, + false if cfg!(feature = "release_max_level_warn") => LevelFilter::Warn, + false if cfg!(feature = "release_max_level_info") => LevelFilter::Info, + false if cfg!(feature = "release_max_level_debug") => LevelFilter::Debug, + false if cfg!(feature = "release_max_level_trace") => LevelFilter::Trace, + _ if cfg!(feature = "max_level_off") => LevelFilter::Off, + _ if cfg!(feature = "max_level_error") => LevelFilter::Error, + _ if cfg!(feature = "max_level_warn") => LevelFilter::Warn, + _ if cfg!(feature = "max_level_info") => LevelFilter::Info, + _ if cfg!(feature = "max_level_debug") => LevelFilter::Debug, + _ => LevelFilter::Trace, +}; + +#[cfg(test)] +mod tests { + use super::{Level, LevelFilter, ParseLevelError, STATIC_MAX_LEVEL}; + + #[test] + fn test_levelfilter_from_str() { + let tests = [ + ("off", Ok(LevelFilter::Off)), + ("error", Ok(LevelFilter::Error)), + ("warn", Ok(LevelFilter::Warn)), + ("info", Ok(LevelFilter::Info)), + ("debug", Ok(LevelFilter::Debug)), + ("trace", Ok(LevelFilter::Trace)), + ("OFF", Ok(LevelFilter::Off)), + ("ERROR", Ok(LevelFilter::Error)), + ("WARN", Ok(LevelFilter::Warn)), + ("INFO", Ok(LevelFilter::Info)), + ("DEBUG", Ok(LevelFilter::Debug)), + ("TRACE", Ok(LevelFilter::Trace)), + ("asdf", Err(ParseLevelError(()))), + ]; + for &(s, ref expected) in &tests { + assert_eq!(expected, &s.parse()); + } + } + + #[test] + fn test_level_from_str() { + let tests = [ + ("OFF", Err(ParseLevelError(()))), + ("error", Ok(Level::Error)), + ("warn", Ok(Level::Warn)), + ("info", Ok(Level::Info)), + ("debug", Ok(Level::Debug)), + ("trace", Ok(Level::Trace)), + ("ERROR", Ok(Level::Error)), + ("WARN", Ok(Level::Warn)), + ("INFO", Ok(Level::Info)), + ("DEBUG", Ok(Level::Debug)), + ("TRACE", Ok(Level::Trace)), + ("asdf", Err(ParseLevelError(()))), + ]; + for &(s, ref expected) in &tests { + assert_eq!(expected, &s.parse()); + } + } + + #[test] + fn test_level_as_str() { + let tests = &[ + (Level::Error, "ERROR"), + (Level::Warn, "WARN"), + (Level::Info, "INFO"), + (Level::Debug, "DEBUG"), + (Level::Trace, "TRACE"), + ]; + for (input, expected) in tests { + assert_eq!(*expected, input.as_str()); + } + } + + #[test] + fn test_level_show() { + assert_eq!("INFO", Level::Info.to_string()); + assert_eq!("ERROR", Level::Error.to_string()); + } + + #[test] + fn test_levelfilter_show() { + assert_eq!("OFF", LevelFilter::Off.to_string()); + assert_eq!("ERROR", LevelFilter::Error.to_string()); + } + + #[test] + fn test_cross_cmp() { + assert!(Level::Debug > LevelFilter::Error); + assert!(LevelFilter::Warn < Level::Trace); + assert!(LevelFilter::Off < Level::Error); + } + + #[test] + fn test_cross_eq() { + assert!(Level::Error == LevelFilter::Error); + assert!(LevelFilter::Off != Level::Error); + assert!(Level::Trace == LevelFilter::Trace); + } + + #[test] + fn test_to_level() { + assert_eq!(Some(Level::Error), LevelFilter::Error.to_level()); + assert_eq!(None, LevelFilter::Off.to_level()); + assert_eq!(Some(Level::Debug), LevelFilter::Debug.to_level()); + } + + #[test] + fn test_to_level_filter() { + assert_eq!(LevelFilter::Error, Level::Error.to_level_filter()); + assert_eq!(LevelFilter::Trace, Level::Trace.to_level_filter()); + } + + #[test] + fn test_level_filter_as_str() { + let tests = &[ + (LevelFilter::Off, "OFF"), + (LevelFilter::Error, "ERROR"), + (LevelFilter::Warn, "WARN"), + (LevelFilter::Info, "INFO"), + (LevelFilter::Debug, "DEBUG"), + (LevelFilter::Trace, "TRACE"), + ]; + for (input, expected) in tests { + assert_eq!(*expected, input.as_str()); + } + } + + #[test] + #[cfg_attr(not(debug_assertions), ignore)] + fn test_static_max_level_debug() { + if cfg!(feature = "max_level_off") { + assert_eq!(STATIC_MAX_LEVEL, LevelFilter::Off); + } else if cfg!(feature = "max_level_error") { + assert_eq!(STATIC_MAX_LEVEL, LevelFilter::Error); + } else if cfg!(feature = "max_level_warn") { + assert_eq!(STATIC_MAX_LEVEL, LevelFilter::Warn); + } else if cfg!(feature = "max_level_info") { + assert_eq!(STATIC_MAX_LEVEL, LevelFilter::Info); + } else if cfg!(feature = "max_level_debug") { + assert_eq!(STATIC_MAX_LEVEL, LevelFilter::Debug); + } else { + assert_eq!(STATIC_MAX_LEVEL, LevelFilter::Trace); + } + } + + #[test] + #[cfg_attr(debug_assertions, ignore)] + fn test_static_max_level_release() { + if cfg!(feature = "release_max_level_off") { + assert_eq!(STATIC_MAX_LEVEL, LevelFilter::Off); + } else if cfg!(feature = "release_max_level_error") { + assert_eq!(STATIC_MAX_LEVEL, LevelFilter::Error); + } else if cfg!(feature = "release_max_level_warn") { + assert_eq!(STATIC_MAX_LEVEL, LevelFilter::Warn); + } else if cfg!(feature = "release_max_level_info") { + assert_eq!(STATIC_MAX_LEVEL, LevelFilter::Info); + } else if cfg!(feature = "release_max_level_debug") { + assert_eq!(STATIC_MAX_LEVEL, LevelFilter::Debug); + } else if cfg!(feature = "release_max_level_trace") { + assert_eq!(STATIC_MAX_LEVEL, LevelFilter::Trace); + } else if cfg!(feature = "max_level_off") { + assert_eq!(STATIC_MAX_LEVEL, LevelFilter::Off); + } else if cfg!(feature = "max_level_error") { + assert_eq!(STATIC_MAX_LEVEL, LevelFilter::Error); + } else if cfg!(feature = "max_level_warn") { + assert_eq!(STATIC_MAX_LEVEL, LevelFilter::Warn); + } else if cfg!(feature = "max_level_info") { + assert_eq!(STATIC_MAX_LEVEL, LevelFilter::Info); + } else if cfg!(feature = "max_level_debug") { + assert_eq!(STATIC_MAX_LEVEL, LevelFilter::Debug); + } else { + assert_eq!(STATIC_MAX_LEVEL, LevelFilter::Trace); + } + } + + #[test] + #[cfg(feature = "std")] + fn test_error_trait() { + use super::SetLoggerError; + let e = SetLoggerError(()); + assert_eq!( + &e.to_string(), + "attempted to set a logger after the logging system \ + was already initialized" + ); + } + + #[test] + fn test_metadata_builder() { + use super::MetadataBuilder; + let target = "myApp"; + let metadata_test = MetadataBuilder::new() + .level(Level::Debug) + .target(target) + .build(); + assert_eq!(metadata_test.level(), Level::Debug); + assert_eq!(metadata_test.target(), "myApp"); + } + + #[test] + fn test_metadata_convenience_builder() { + use super::Metadata; + let target = "myApp"; + let metadata_test = Metadata::builder() + .level(Level::Debug) + .target(target) + .build(); + assert_eq!(metadata_test.level(), Level::Debug); + assert_eq!(metadata_test.target(), "myApp"); + } + + #[test] + fn test_record_builder() { + use super::{MetadataBuilder, RecordBuilder}; + let target = "myApp"; + let metadata = MetadataBuilder::new().target(target).build(); + let fmt_args = format_args!("hello"); + let record_test = RecordBuilder::new() + .args(fmt_args) + .metadata(metadata) + .module_path(Some("foo")) + .file(Some("bar")) + .line(Some(30)) + .build(); + assert_eq!(record_test.metadata().target(), "myApp"); + assert_eq!(record_test.module_path(), Some("foo")); + assert_eq!(record_test.file(), Some("bar")); + assert_eq!(record_test.line(), Some(30)); + } + + #[test] + fn test_record_convenience_builder() { + use super::{Metadata, Record}; + let target = "myApp"; + let metadata = Metadata::builder().target(target).build(); + let fmt_args = format_args!("hello"); + let record_test = Record::builder() + .args(fmt_args) + .metadata(metadata) + .module_path(Some("foo")) + .file(Some("bar")) + .line(Some(30)) + .build(); + assert_eq!(record_test.target(), "myApp"); + assert_eq!(record_test.module_path(), Some("foo")); + assert_eq!(record_test.file(), Some("bar")); + assert_eq!(record_test.line(), Some(30)); + } + + #[test] + fn test_record_complete_builder() { + use super::{Level, Record}; + let target = "myApp"; + let record_test = Record::builder() + .module_path(Some("foo")) + .file(Some("bar")) + .line(Some(30)) + .target(target) + .level(Level::Error) + .build(); + assert_eq!(record_test.target(), "myApp"); + assert_eq!(record_test.level(), Level::Error); + assert_eq!(record_test.module_path(), Some("foo")); + assert_eq!(record_test.file(), Some("bar")); + assert_eq!(record_test.line(), Some(30)); + } + + #[test] + #[cfg(feature = "kv")] + fn test_record_key_values_builder() { + use super::Record; + use crate::kv::{self, VisitSource}; + + struct TestVisitSource { + seen_pairs: usize, + } + + impl<'kvs> VisitSource<'kvs> for TestVisitSource { + fn visit_pair( + &mut self, + _: kv::Key<'kvs>, + _: kv::Value<'kvs>, + ) -> Result<(), kv::Error> { + self.seen_pairs += 1; + Ok(()) + } + } + + let kvs: &[(&str, i32)] = &[("a", 1), ("b", 2)]; + let record_test = Record::builder().key_values(&kvs).build(); + + let mut visitor = TestVisitSource { seen_pairs: 0 }; + + record_test.key_values().visit(&mut visitor).unwrap(); + + assert_eq!(2, visitor.seen_pairs); + } + + #[test] + #[cfg(feature = "kv")] + fn test_record_key_values_get_coerce() { + use super::Record; + + let kvs: &[(&str, &str)] = &[("a", "1"), ("b", "2")]; + let record = Record::builder().key_values(&kvs).build(); + + assert_eq!( + "2", + record + .key_values() + .get("b".into()) + .expect("missing key") + .to_borrowed_str() + .expect("invalid value") + ); + } + + // Test that the `impl Log for Foo` blocks work + // This test mostly operates on a type level, so failures will be compile errors + #[test] + fn test_foreign_impl() { + use super::Log; + #[cfg(feature = "std")] + use std::sync::Arc; + + fn assert_is_log<T: Log + ?Sized>() {} + + assert_is_log::<&dyn Log>(); + + #[cfg(feature = "std")] + assert_is_log::<Box<dyn Log>>(); + + #[cfg(feature = "std")] + assert_is_log::<Arc<dyn Log>>(); + + // Assert these statements for all T: Log + ?Sized + #[allow(unused)] + fn forall<T: Log + ?Sized>() { + #[cfg(feature = "std")] + assert_is_log::<Box<T>>(); + + assert_is_log::<&T>(); + + #[cfg(feature = "std")] + assert_is_log::<Arc<T>>(); + } + } +} diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/log/src/macros.rs b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/log/src/macros.rs new file mode 100644 index 0000000..87693f2 --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/log/src/macros.rs @@ -0,0 +1,367 @@ +// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +/// The standard logging macro. +/// +/// This macro will generically log with the specified `Level` and `format!` +/// based argument list. +/// +/// # Examples +/// +/// ``` +/// use log::{log, Level}; +/// +/// # fn main() { +/// let data = (42, "Forty-two"); +/// let private_data = "private"; +/// +/// log!(Level::Error, "Received errors: {}, {}", data.0, data.1); +/// log!(target: "app_events", Level::Warn, "App warning: {}, {}, {}", +/// data.0, data.1, private_data); +/// # } +/// ``` +#[macro_export] +macro_rules! log { + // log!(target: "my_target", Level::Info, key1:? = 42, key2 = true; "a {} event", "log"); + (target: $target:expr, $lvl:expr, $($key:tt $(:$capture:tt)? $(= $value:expr)?),+; $($arg:tt)+) => ({ + let lvl = $lvl; + if lvl <= $crate::STATIC_MAX_LEVEL && lvl <= $crate::max_level() { + $crate::__private_api::log::<&_>( + $crate::__private_api::format_args!($($arg)+), + lvl, + &($target, $crate::__private_api::module_path!(), $crate::__private_api::loc()), + &[$(($crate::__log_key!($key), $crate::__log_value!($key $(:$capture)* = $($value)*))),+] + ); + } + }); + + // log!(target: "my_target", Level::Info, "a {} event", "log"); + (target: $target:expr, $lvl:expr, $($arg:tt)+) => ({ + let lvl = $lvl; + if lvl <= $crate::STATIC_MAX_LEVEL && lvl <= $crate::max_level() { + $crate::__private_api::log( + $crate::__private_api::format_args!($($arg)+), + lvl, + &($target, $crate::__private_api::module_path!(), $crate::__private_api::loc()), + (), + ); + } + }); + + // log!(Level::Info, "a log event") + ($lvl:expr, $($arg:tt)+) => ($crate::log!(target: $crate::__private_api::module_path!(), $lvl, $($arg)+)); +} + +/// Logs a message at the error level. +/// +/// # Examples +/// +/// ``` +/// use log::error; +/// +/// # fn main() { +/// let (err_info, port) = ("No connection", 22); +/// +/// error!("Error: {err_info} on port {port}"); +/// error!(target: "app_events", "App Error: {err_info}, Port: {port}"); +/// # } +/// ``` +#[macro_export] +macro_rules! error { + // error!(target: "my_target", key1 = 42, key2 = true; "a {} event", "log") + // error!(target: "my_target", "a {} event", "log") + (target: $target:expr, $($arg:tt)+) => ($crate::log!(target: $target, $crate::Level::Error, $($arg)+)); + + // error!("a {} event", "log") + ($($arg:tt)+) => ($crate::log!($crate::Level::Error, $($arg)+)) +} + +/// Logs a message at the warn level. +/// +/// # Examples +/// +/// ``` +/// use log::warn; +/// +/// # fn main() { +/// let warn_description = "Invalid Input"; +/// +/// warn!("Warning! {warn_description}!"); +/// warn!(target: "input_events", "App received warning: {warn_description}"); +/// # } +/// ``` +#[macro_export] +macro_rules! warn { + // warn!(target: "my_target", key1 = 42, key2 = true; "a {} event", "log") + // warn!(target: "my_target", "a {} event", "log") + (target: $target:expr, $($arg:tt)+) => ($crate::log!(target: $target, $crate::Level::Warn, $($arg)+)); + + // warn!("a {} event", "log") + ($($arg:tt)+) => ($crate::log!($crate::Level::Warn, $($arg)+)) +} + +/// Logs a message at the info level. +/// +/// # Examples +/// +/// ``` +/// use log::info; +/// +/// # fn main() { +/// # struct Connection { port: u32, speed: f32 } +/// let conn_info = Connection { port: 40, speed: 3.20 }; +/// +/// info!("Connected to port {} at {} Mb/s", conn_info.port, conn_info.speed); +/// info!(target: "connection_events", "Successful connection, port: {}, speed: {}", +/// conn_info.port, conn_info.speed); +/// # } +/// ``` +#[macro_export] +macro_rules! info { + // info!(target: "my_target", key1 = 42, key2 = true; "a {} event", "log") + // info!(target: "my_target", "a {} event", "log") + (target: $target:expr, $($arg:tt)+) => ($crate::log!(target: $target, $crate::Level::Info, $($arg)+)); + + // info!("a {} event", "log") + ($($arg:tt)+) => ($crate::log!($crate::Level::Info, $($arg)+)) +} + +/// Logs a message at the debug level. +/// +/// # Examples +/// +/// ``` +/// use log::debug; +/// +/// # fn main() { +/// # struct Position { x: f32, y: f32 } +/// let pos = Position { x: 3.234, y: -1.223 }; +/// +/// debug!("New position: x: {}, y: {}", pos.x, pos.y); +/// debug!(target: "app_events", "New position: x: {}, y: {}", pos.x, pos.y); +/// # } +/// ``` +#[macro_export] +macro_rules! debug { + // debug!(target: "my_target", key1 = 42, key2 = true; "a {} event", "log") + // debug!(target: "my_target", "a {} event", "log") + (target: $target:expr, $($arg:tt)+) => ($crate::log!(target: $target, $crate::Level::Debug, $($arg)+)); + + // debug!("a {} event", "log") + ($($arg:tt)+) => ($crate::log!($crate::Level::Debug, $($arg)+)) +} + +/// Logs a message at the trace level. +/// +/// # Examples +/// +/// ``` +/// use log::trace; +/// +/// # fn main() { +/// # struct Position { x: f32, y: f32 } +/// let pos = Position { x: 3.234, y: -1.223 }; +/// +/// trace!("Position is: x: {}, y: {}", pos.x, pos.y); +/// trace!(target: "app_events", "x is {} and y is {}", +/// if pos.x >= 0.0 { "positive" } else { "negative" }, +/// if pos.y >= 0.0 { "positive" } else { "negative" }); +/// # } +/// ``` +#[macro_export] +macro_rules! trace { + // trace!(target: "my_target", key1 = 42, key2 = true; "a {} event", "log") + // trace!(target: "my_target", "a {} event", "log") + (target: $target:expr, $($arg:tt)+) => ($crate::log!(target: $target, $crate::Level::Trace, $($arg)+)); + + // trace!("a {} event", "log") + ($($arg:tt)+) => ($crate::log!($crate::Level::Trace, $($arg)+)) +} + +/// Determines if a message logged at the specified level in that module will +/// be logged. +/// +/// This can be used to avoid expensive computation of log message arguments if +/// the message would be ignored anyway. +/// +/// # Examples +/// +/// ``` +/// use log::Level::Debug; +/// use log::{debug, log_enabled}; +/// +/// # fn foo() { +/// if log_enabled!(Debug) { +/// let data = expensive_call(); +/// debug!("expensive debug data: {} {}", data.x, data.y); +/// } +/// if log_enabled!(target: "Global", Debug) { +/// let data = expensive_call(); +/// debug!(target: "Global", "expensive debug data: {} {}", data.x, data.y); +/// } +/// # } +/// # struct Data { x: u32, y: u32 } +/// # fn expensive_call() -> Data { Data { x: 0, y: 0 } } +/// # fn main() {} +/// ``` +#[macro_export] +macro_rules! log_enabled { + (target: $target:expr, $lvl:expr) => {{ + let lvl = $lvl; + lvl <= $crate::STATIC_MAX_LEVEL + && lvl <= $crate::max_level() + && $crate::__private_api::enabled(lvl, $target) + }}; + ($lvl:expr) => { + $crate::log_enabled!(target: $crate::__private_api::module_path!(), $lvl) + }; +} + +// These macros use a pattern of #[cfg]s to produce nicer error +// messages when log features aren't available + +#[doc(hidden)] +#[macro_export] +#[cfg(feature = "kv")] +macro_rules! __log_key { + // key1 = 42 + ($($args:ident)*) => { + $crate::__private_api::stringify!($($args)*) + }; + // "key1" = 42 + ($($args:expr)*) => { + $($args)* + }; +} + +#[doc(hidden)] +#[macro_export] +#[cfg(not(feature = "kv"))] +macro_rules! __log_key { + ($($args:tt)*) => { + compile_error!("key value support requires the `kv` feature of `log`") + }; +} + +#[doc(hidden)] +#[macro_export] +#[cfg(feature = "kv")] +macro_rules! __log_value { + // Entrypoint + ($key:tt = $args:expr) => { + $crate::__log_value!(($args):value) + }; + ($key:tt :$capture:tt = $args:expr) => { + $crate::__log_value!(($args):$capture) + }; + ($key:ident =) => { + $crate::__log_value!(($key):value) + }; + ($key:ident :$capture:tt =) => { + $crate::__log_value!(($key):$capture) + }; + // ToValue + (($args:expr):value) => { + $crate::__private_api::capture_to_value(&&$args) + }; + // Debug + (($args:expr):?) => { + $crate::__private_api::capture_debug(&&$args) + }; + (($args:expr):debug) => { + $crate::__private_api::capture_debug(&&$args) + }; + // Display + (($args:expr):%) => { + $crate::__private_api::capture_display(&&$args) + }; + (($args:expr):display) => { + $crate::__private_api::capture_display(&&$args) + }; + //Error + (($args:expr):err) => { + $crate::__log_value_error!($args) + }; + // sval::Value + (($args:expr):sval) => { + $crate::__log_value_sval!($args) + }; + // serde::Serialize + (($args:expr):serde) => { + $crate::__log_value_serde!($args) + }; +} + +#[doc(hidden)] +#[macro_export] +#[cfg(not(feature = "kv"))] +macro_rules! __log_value { + ($($args:tt)*) => { + compile_error!("key value support requires the `kv` feature of `log`") + }; +} + +#[doc(hidden)] +#[macro_export] +#[cfg(feature = "kv_sval")] +macro_rules! __log_value_sval { + ($args:expr) => { + $crate::__private_api::capture_sval(&&$args) + }; +} + +#[doc(hidden)] +#[macro_export] +#[cfg(not(feature = "kv_sval"))] +macro_rules! __log_value_sval { + ($args:expr) => { + compile_error!("capturing values as `sval::Value` requites the `kv_sval` feature of `log`") + }; +} + +#[doc(hidden)] +#[macro_export] +#[cfg(feature = "kv_serde")] +macro_rules! __log_value_serde { + ($args:expr) => { + $crate::__private_api::capture_serde(&&$args) + }; +} + +#[doc(hidden)] +#[macro_export] +#[cfg(not(feature = "kv_serde"))] +macro_rules! __log_value_serde { + ($args:expr) => { + compile_error!( + "capturing values as `serde::Serialize` requites the `kv_serde` feature of `log`" + ) + }; +} + +#[doc(hidden)] +#[macro_export] +#[cfg(feature = "kv_std")] +macro_rules! __log_value_error { + ($args:expr) => { + $crate::__private_api::capture_error(&$args) + }; +} + +#[doc(hidden)] +#[macro_export] +#[cfg(not(feature = "kv_std"))] +macro_rules! __log_value_error { + ($args:expr) => { + compile_error!( + "capturing values as `std::error::Error` requites the `kv_std` feature of `log`" + ) + }; +} diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/log/src/serde.rs b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/log/src/serde.rs new file mode 100644 index 0000000..63bef7f --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/log/src/serde.rs @@ -0,0 +1,397 @@ +#![cfg(feature = "serde")] + +use serde::de::{ + Deserialize, DeserializeSeed, Deserializer, EnumAccess, Error, Unexpected, VariantAccess, + Visitor, +}; +use serde::ser::{Serialize, Serializer}; + +use crate::{Level, LevelFilter, LOG_LEVEL_NAMES}; + +use std::fmt; +use std::str::{self, FromStr}; + +// The Deserialize impls are handwritten to be case insensitive using FromStr. + +impl Serialize for Level { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: Serializer, + { + match *self { + Level::Error => serializer.serialize_unit_variant("Level", 0, "ERROR"), + Level::Warn => serializer.serialize_unit_variant("Level", 1, "WARN"), + Level::Info => serializer.serialize_unit_variant("Level", 2, "INFO"), + Level::Debug => serializer.serialize_unit_variant("Level", 3, "DEBUG"), + Level::Trace => serializer.serialize_unit_variant("Level", 4, "TRACE"), + } + } +} + +impl<'de> Deserialize<'de> for Level { + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where + D: Deserializer<'de>, + { + struct LevelIdentifier; + + impl<'de> Visitor<'de> for LevelIdentifier { + type Value = Level; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("log level") + } + + fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E> + where + E: Error, + { + let variant = LOG_LEVEL_NAMES[1..] + .get(v as usize) + .ok_or_else(|| Error::invalid_value(Unexpected::Unsigned(v), &self))?; + + self.visit_str(variant) + } + + fn visit_str<E>(self, s: &str) -> Result<Self::Value, E> + where + E: Error, + { + // Case insensitive. + FromStr::from_str(s).map_err(|_| Error::unknown_variant(s, &LOG_LEVEL_NAMES[1..])) + } + + fn visit_bytes<E>(self, value: &[u8]) -> Result<Self::Value, E> + where + E: Error, + { + let variant = str::from_utf8(value) + .map_err(|_| Error::invalid_value(Unexpected::Bytes(value), &self))?; + + self.visit_str(variant) + } + } + + impl<'de> DeserializeSeed<'de> for LevelIdentifier { + type Value = Level; + + fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error> + where + D: Deserializer<'de>, + { + deserializer.deserialize_identifier(LevelIdentifier) + } + } + + struct LevelEnum; + + impl<'de> Visitor<'de> for LevelEnum { + type Value = Level; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("log level") + } + + fn visit_enum<A>(self, value: A) -> Result<Self::Value, A::Error> + where + A: EnumAccess<'de>, + { + let (level, variant) = value.variant_seed(LevelIdentifier)?; + // Every variant is a unit variant. + variant.unit_variant()?; + Ok(level) + } + } + + deserializer.deserialize_enum("Level", &LOG_LEVEL_NAMES[1..], LevelEnum) + } +} + +impl Serialize for LevelFilter { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: Serializer, + { + match *self { + LevelFilter::Off => serializer.serialize_unit_variant("LevelFilter", 0, "OFF"), + LevelFilter::Error => serializer.serialize_unit_variant("LevelFilter", 1, "ERROR"), + LevelFilter::Warn => serializer.serialize_unit_variant("LevelFilter", 2, "WARN"), + LevelFilter::Info => serializer.serialize_unit_variant("LevelFilter", 3, "INFO"), + LevelFilter::Debug => serializer.serialize_unit_variant("LevelFilter", 4, "DEBUG"), + LevelFilter::Trace => serializer.serialize_unit_variant("LevelFilter", 5, "TRACE"), + } + } +} + +impl<'de> Deserialize<'de> for LevelFilter { + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where + D: Deserializer<'de>, + { + struct LevelFilterIdentifier; + + impl<'de> Visitor<'de> for LevelFilterIdentifier { + type Value = LevelFilter; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("log level filter") + } + + fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E> + where + E: Error, + { + let variant = LOG_LEVEL_NAMES + .get(v as usize) + .ok_or_else(|| Error::invalid_value(Unexpected::Unsigned(v), &self))?; + + self.visit_str(variant) + } + + fn visit_str<E>(self, s: &str) -> Result<Self::Value, E> + where + E: Error, + { + // Case insensitive. + FromStr::from_str(s).map_err(|_| Error::unknown_variant(s, &LOG_LEVEL_NAMES)) + } + + fn visit_bytes<E>(self, value: &[u8]) -> Result<Self::Value, E> + where + E: Error, + { + let variant = str::from_utf8(value) + .map_err(|_| Error::invalid_value(Unexpected::Bytes(value), &self))?; + + self.visit_str(variant) + } + } + + impl<'de> DeserializeSeed<'de> for LevelFilterIdentifier { + type Value = LevelFilter; + + fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error> + where + D: Deserializer<'de>, + { + deserializer.deserialize_identifier(LevelFilterIdentifier) + } + } + + struct LevelFilterEnum; + + impl<'de> Visitor<'de> for LevelFilterEnum { + type Value = LevelFilter; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("log level filter") + } + + fn visit_enum<A>(self, value: A) -> Result<Self::Value, A::Error> + where + A: EnumAccess<'de>, + { + let (level_filter, variant) = value.variant_seed(LevelFilterIdentifier)?; + // Every variant is a unit variant. + variant.unit_variant()?; + Ok(level_filter) + } + } + + deserializer.deserialize_enum("LevelFilter", &LOG_LEVEL_NAMES, LevelFilterEnum) + } +} + +#[cfg(test)] +mod tests { + use crate::{Level, LevelFilter}; + use serde_test::{assert_de_tokens, assert_de_tokens_error, assert_tokens, Token}; + + fn level_token(variant: &'static str) -> Token { + Token::UnitVariant { + name: "Level", + variant, + } + } + + fn level_bytes_tokens(variant: &'static [u8]) -> [Token; 3] { + [ + Token::Enum { name: "Level" }, + Token::Bytes(variant), + Token::Unit, + ] + } + + fn level_variant_tokens(variant: u32) -> [Token; 3] { + [ + Token::Enum { name: "Level" }, + Token::U32(variant), + Token::Unit, + ] + } + + fn level_filter_token(variant: &'static str) -> Token { + Token::UnitVariant { + name: "LevelFilter", + variant, + } + } + + fn level_filter_bytes_tokens(variant: &'static [u8]) -> [Token; 3] { + [ + Token::Enum { + name: "LevelFilter", + }, + Token::Bytes(variant), + Token::Unit, + ] + } + + fn level_filter_variant_tokens(variant: u32) -> [Token; 3] { + [ + Token::Enum { + name: "LevelFilter", + }, + Token::U32(variant), + Token::Unit, + ] + } + + #[test] + fn test_level_ser_de() { + let cases = &[ + (Level::Error, [level_token("ERROR")]), + (Level::Warn, [level_token("WARN")]), + (Level::Info, [level_token("INFO")]), + (Level::Debug, [level_token("DEBUG")]), + (Level::Trace, [level_token("TRACE")]), + ]; + + for (s, expected) in cases { + assert_tokens(s, expected); + } + } + + #[test] + fn test_level_case_insensitive() { + let cases = &[ + (Level::Error, [level_token("error")]), + (Level::Warn, [level_token("warn")]), + (Level::Info, [level_token("info")]), + (Level::Debug, [level_token("debug")]), + (Level::Trace, [level_token("trace")]), + ]; + + for (s, expected) in cases { + assert_de_tokens(s, expected); + } + } + + #[test] + fn test_level_de_bytes() { + let cases = &[ + (Level::Error, level_bytes_tokens(b"ERROR")), + (Level::Warn, level_bytes_tokens(b"WARN")), + (Level::Info, level_bytes_tokens(b"INFO")), + (Level::Debug, level_bytes_tokens(b"DEBUG")), + (Level::Trace, level_bytes_tokens(b"TRACE")), + ]; + + for (value, tokens) in cases { + assert_de_tokens(value, tokens); + } + } + + #[test] + fn test_level_de_variant_index() { + let cases = &[ + (Level::Error, level_variant_tokens(0)), + (Level::Warn, level_variant_tokens(1)), + (Level::Info, level_variant_tokens(2)), + (Level::Debug, level_variant_tokens(3)), + (Level::Trace, level_variant_tokens(4)), + ]; + + for (value, tokens) in cases { + assert_de_tokens(value, tokens); + } + } + + #[test] + fn test_level_de_error() { + let msg = "unknown variant `errorx`, expected one of \ + `ERROR`, `WARN`, `INFO`, `DEBUG`, `TRACE`"; + assert_de_tokens_error::<Level>(&[level_token("errorx")], msg); + } + + #[test] + fn test_level_filter_ser_de() { + let cases = &[ + (LevelFilter::Off, [level_filter_token("OFF")]), + (LevelFilter::Error, [level_filter_token("ERROR")]), + (LevelFilter::Warn, [level_filter_token("WARN")]), + (LevelFilter::Info, [level_filter_token("INFO")]), + (LevelFilter::Debug, [level_filter_token("DEBUG")]), + (LevelFilter::Trace, [level_filter_token("TRACE")]), + ]; + + for (s, expected) in cases { + assert_tokens(s, expected); + } + } + + #[test] + fn test_level_filter_case_insensitive() { + let cases = &[ + (LevelFilter::Off, [level_filter_token("off")]), + (LevelFilter::Error, [level_filter_token("error")]), + (LevelFilter::Warn, [level_filter_token("warn")]), + (LevelFilter::Info, [level_filter_token("info")]), + (LevelFilter::Debug, [level_filter_token("debug")]), + (LevelFilter::Trace, [level_filter_token("trace")]), + ]; + + for (s, expected) in cases { + assert_de_tokens(s, expected); + } + } + + #[test] + fn test_level_filter_de_bytes() { + let cases = &[ + (LevelFilter::Off, level_filter_bytes_tokens(b"OFF")), + (LevelFilter::Error, level_filter_bytes_tokens(b"ERROR")), + (LevelFilter::Warn, level_filter_bytes_tokens(b"WARN")), + (LevelFilter::Info, level_filter_bytes_tokens(b"INFO")), + (LevelFilter::Debug, level_filter_bytes_tokens(b"DEBUG")), + (LevelFilter::Trace, level_filter_bytes_tokens(b"TRACE")), + ]; + + for (value, tokens) in cases { + assert_de_tokens(value, tokens); + } + } + + #[test] + fn test_level_filter_de_variant_index() { + let cases = &[ + (LevelFilter::Off, level_filter_variant_tokens(0)), + (LevelFilter::Error, level_filter_variant_tokens(1)), + (LevelFilter::Warn, level_filter_variant_tokens(2)), + (LevelFilter::Info, level_filter_variant_tokens(3)), + (LevelFilter::Debug, level_filter_variant_tokens(4)), + (LevelFilter::Trace, level_filter_variant_tokens(5)), + ]; + + for (value, tokens) in cases { + assert_de_tokens(value, tokens); + } + } + + #[test] + fn test_level_filter_de_error() { + let msg = "unknown variant `errorx`, expected one of \ + `OFF`, `ERROR`, `WARN`, `INFO`, `DEBUG`, `TRACE`"; + assert_de_tokens_error::<LevelFilter>(&[level_filter_token("errorx")], msg); + } +} diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/log/triagebot.toml b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/log/triagebot.toml new file mode 100644 index 0000000..fa0824a --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/log/triagebot.toml @@ -0,0 +1 @@ +[assign] diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/polonius-engine/.cargo-checksum.json b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/polonius-engine/.cargo-checksum.json new file mode 100644 index 0000000..bcbce9e --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/polonius-engine/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.toml":"d4ff1d54f23cde7d35174635e8db5e6aee0d61b846d2fccbf0d28146246b3a28","README.md":"fc6752d50a57acbf3d65581c0d2f3d0da8c5fef5735a12857660c9e9687751d0","src/facts.rs":"9fad471b6ba5f63b8a1367002fee2b27fc3b3b893680eca390bc2616718f8915","src/lib.rs":"19ef0fd2d054b3a48c11ff4007734b7940ca739d3a9d5083d3a03b4d982cdb99","src/output/datafrog_opt.rs":"c75fa04ed1cc1a5b59f9405ce959af5950d37ae57876cc9510adc7c013b25af5","src/output/initialization.rs":"b9665c1397ff5e1cc1a93e9645bec0bed672ea9822c4dd32fc545ecbd3f80258","src/output/liveness.rs":"b68c9edd17feebff7d0b006caaf8197b5c30320b1a8cdcbe653bd7218954dd4f","src/output/location_insensitive.rs":"eb7c495ec38768104b8877de66341aaca210cdad824cae948f3fd7cf4ba858d0","src/output/mod.rs":"968f8547954a4444f59f3c056a9b742aa59ede3a90bb9b6fe08ba506fcc6bce5","src/output/naive.rs":"b345c2beb8a2f79bc482d131954bea4f23e53a3ce8270f64b8e940f0de376730"},"package":"c4e8e505342045d397d0b6674dcb82d6faf5cf40484d30eeb88fc82ef14e903f"}
\ No newline at end of file diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/polonius-engine/Cargo.toml b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/polonius-engine/Cargo.toml new file mode 100644 index 0000000..e3a8f74 --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/polonius-engine/Cargo.toml @@ -0,0 +1,29 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies +# +# If you believe there's an error in this file please file an +# issue against the rust-lang/cargo repository. If you're +# editing this file be aware that the upstream Cargo.toml +# will likely look very different (and much more reasonable) + +[package] +name = "polonius-engine" +version = "0.13.0" +authors = ["The Rust Project Developers", "Polonius Developers"] +description = "Core definition for the Rust borrow checker" +readme = "README.md" +keywords = ["compiler", "borrowck", "datalog"] +license = "Apache-2.0/MIT" +repository = "https://github.com/rust-lang-nursery/polonius" +[dependencies.datafrog] +version = "2.0.0" + +[dependencies.log] +version = "0.4" + +[dependencies.rustc-hash] +version = "1.0.0" diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/polonius-engine/README.md b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/polonius-engine/README.md new file mode 100644 index 0000000..d88295e --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/polonius-engine/README.md @@ -0,0 +1,6 @@ +This is a core library that models the borrow check. It implements the +analysis [described in this blogpost][post]. This library is intended +for use both by rustc and by the polonius crate, which is a distinct +front-end intended for testing, profiling, etc. + +[post]: http://smallcultfollowing.com/babysteps/blog/2018/04/27/an-alias-based-formulation-of-the-borrow-checker/ diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/polonius-engine/src/facts.rs b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/polonius-engine/src/facts.rs new file mode 100644 index 0000000..442ba18 --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/polonius-engine/src/facts.rs @@ -0,0 +1,129 @@ +use std::fmt::Debug; +use std::hash::Hash; + +/// The "facts" which are the basis of the NLL borrow analysis. +#[derive(Clone, Debug)] +pub struct AllFacts<T: FactTypes> { + /// `loan_issued_at(origin, loan, point)` indicates that the `loan` was "issued" + /// at the given `point`, creating a reference with the `origin`. + /// Effectively, `origin` may refer to data from `loan` starting at `point` (this is usually + /// the point *after* a borrow rvalue). + pub loan_issued_at: Vec<(T::Origin, T::Loan, T::Point)>, + + /// `universal_region(origin)` -- this is a "free region" within fn body + pub universal_region: Vec<T::Origin>, + + /// `cfg_edge(point1, point2)` for each edge `point1 -> point2` in the control flow + pub cfg_edge: Vec<(T::Point, T::Point)>, + + /// `loan_killed_at(loan, point)` when some prefix of the path borrowed at `loan` + /// is assigned at `point`. + /// Indicates that the path borrowed by the `loan` has changed in some way that the loan no + /// longer needs to be tracked. (In particular, mutations to the path that was borrowed + /// no longer invalidate the loan) + pub loan_killed_at: Vec<(T::Loan, T::Point)>, + + /// `subset_base(origin1, origin2, point)` when we require `origin1@point: origin2@point`. + /// Indicates that `origin1 <= origin2` -- i.e., the set of loans in `origin1` are a subset + /// of those in `origin2`. + pub subset_base: Vec<(T::Origin, T::Origin, T::Point)>, + + /// `loan_invalidated_at(point, loan)` indicates that the `loan` is invalidated by some action + /// taking place at `point`; if any origin that references this loan is live, this is an error. + pub loan_invalidated_at: Vec<(T::Point, T::Loan)>, + + /// `var_used_at(var, point)` when the variable `var` is used for anything + /// but a drop at `point` + pub var_used_at: Vec<(T::Variable, T::Point)>, + + /// `var_defined_at(var, point)` when the variable `var` is overwritten at `point` + pub var_defined_at: Vec<(T::Variable, T::Point)>, + + /// `var_dropped_at(var, point)` when the variable `var` is used in a drop at `point` + pub var_dropped_at: Vec<(T::Variable, T::Point)>, + + /// `use_of_var_derefs_origin(variable, origin)`: References with the given + /// `origin` may be dereferenced when the `variable` is used. + /// + /// In rustc, we generate this whenever the type of the variable includes the + /// given origin. + pub use_of_var_derefs_origin: Vec<(T::Variable, T::Origin)>, + + /// `drop_of_var_derefs_origin(var, origin)` when the type of `var` includes + /// the `origin` and uses it when dropping + pub drop_of_var_derefs_origin: Vec<(T::Variable, T::Origin)>, + + /// `child_path(child, parent)` when the path `child` is the direct child of + /// `parent`, e.g. `child_path(x.y, x)`, but not `child_path(x.y.z, x)`. + pub child_path: Vec<(T::Path, T::Path)>, + + /// `path_is_var(path, var)` the root path `path` starting in variable `var`. + pub path_is_var: Vec<(T::Path, T::Variable)>, + + /// `path_assigned_at_base(path, point)` when the `path` was initialized at point + /// `point`. This fact is only emitted for a prefix `path`, and not for the + /// implicit initialization of all of `path`'s children. E.g. a statement like + /// `x.y = 3` at `point` would give the fact `path_assigned_at_base(x.y, point)` (but + /// neither `path_assigned_at_base(x.y.z, point)` nor `path_assigned_at_base(x, point)`). + pub path_assigned_at_base: Vec<(T::Path, T::Point)>, + + /// `path_moved_at_base(path, point)` when the `path` was moved at `point`. The + /// same logic is applied as for `path_assigned_at_base` above. + pub path_moved_at_base: Vec<(T::Path, T::Point)>, + + /// `path_accessed_at_base(path, point)` when the `path` was accessed at point + /// `point`. The same logic as for `path_assigned_at_base` and `path_moved_at_base` applies. + pub path_accessed_at_base: Vec<(T::Path, T::Point)>, + + /// These reflect the `'a: 'b` relations that are either declared by the user on function + /// declarations or which are inferred via implied bounds. + /// For example: `fn foo<'a, 'b: 'a, 'c>(x: &'c &'a u32)` would have two entries: + /// - one for the user-supplied subset `'b: 'a` + /// - and one for the `'a: 'c` implied bound from the `x` parameter, + /// (note that the transitive relation `'b: 'c` is not necessarily included + /// explicitly, but rather inferred by polonius). + pub known_placeholder_subset: Vec<(T::Origin, T::Origin)>, + + /// `placeholder(origin, loan)` describes a placeholder `origin`, with its associated + /// placeholder `loan`. + pub placeholder: Vec<(T::Origin, T::Loan)>, +} + +impl<T: FactTypes> Default for AllFacts<T> { + fn default() -> Self { + AllFacts { + loan_issued_at: Vec::default(), + universal_region: Vec::default(), + cfg_edge: Vec::default(), + loan_killed_at: Vec::default(), + subset_base: Vec::default(), + loan_invalidated_at: Vec::default(), + var_used_at: Vec::default(), + var_defined_at: Vec::default(), + var_dropped_at: Vec::default(), + use_of_var_derefs_origin: Vec::default(), + drop_of_var_derefs_origin: Vec::default(), + child_path: Vec::default(), + path_is_var: Vec::default(), + path_assigned_at_base: Vec::default(), + path_moved_at_base: Vec::default(), + path_accessed_at_base: Vec::default(), + known_placeholder_subset: Vec::default(), + placeholder: Vec::default(), + } + } +} + +pub trait Atom: + From<usize> + Into<usize> + Copy + Clone + Debug + Eq + Ord + Hash + 'static +{ + fn index(self) -> usize; +} + +pub trait FactTypes: Copy + Clone + Debug { + type Origin: Atom; + type Loan: Atom; + type Point: Atom; + type Variable: Atom; + type Path: Atom; +} diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/polonius-engine/src/lib.rs b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/polonius-engine/src/lib.rs new file mode 100644 index 0000000..0926be8 --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/polonius-engine/src/lib.rs @@ -0,0 +1,16 @@ +/// Contains the core of the Polonius borrow checking engine. +/// Input is fed in via AllFacts, and outputs are returned via Output +extern crate datafrog; +#[macro_use] +extern crate log; +extern crate rustc_hash; + +mod facts; +mod output; + +// Reexports of facts +pub use facts::AllFacts; +pub use facts::Atom; +pub use facts::FactTypes; +pub use output::Algorithm; +pub use output::Output; diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/polonius-engine/src/output/datafrog_opt.rs b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/polonius-engine/src/output/datafrog_opt.rs new file mode 100644 index 0000000..da9c343 --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/polonius-engine/src/output/datafrog_opt.rs @@ -0,0 +1,495 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use datafrog::{Iteration, Relation, RelationLeaper}; +use std::time::Instant; + +use crate::facts::FactTypes; +use crate::output::{Context, Output}; + +pub(super) fn compute<T: FactTypes>( + ctx: &Context<'_, T>, + result: &mut Output<T>, +) -> ( + Relation<(T::Loan, T::Point)>, + Relation<(T::Origin, T::Origin, T::Point)>, +) { + let timer = Instant::now(); + + let (errors, subset_errors) = { + // Static inputs + let origin_live_on_entry_rel = &ctx.origin_live_on_entry; + let cfg_edge_rel = &ctx.cfg_edge; + let loan_killed_at = &ctx.loan_killed_at; + let known_placeholder_subset = &ctx.known_placeholder_subset; + let placeholder_origin = &ctx.placeholder_origin; + + // Create a new iteration context, ... + let mut iteration = Iteration::new(); + + // `loan_invalidated_at` facts, stored ready for joins + let loan_invalidated_at = + iteration.variable::<((T::Loan, T::Point), ())>("loan_invalidated_at"); + + // we need `origin_live_on_entry` in both variable and relation forms, + // (respectively, for join and antijoin). + let origin_live_on_entry_var = + iteration.variable::<((T::Origin, T::Point), ())>("origin_live_on_entry"); + + // `loan_issued_at` input but organized for join + let loan_issued_at_op = + iteration.variable::<((T::Origin, T::Point), T::Loan)>("loan_issued_at_op"); + + // .decl subset(origin1, origin2, point) + // + // Indicates that `origin1: origin2` at `point`. + let subset_o1p = iteration.variable::<((T::Origin, T::Point), T::Origin)>("subset_o1p"); + + // .decl origin_contains_loan_on_entry(origin, loan, point) + // + // At `point`, things with `origin` may depend on data from `loan`. + let origin_contains_loan_on_entry_op = iteration + .variable::<((T::Origin, T::Point), T::Loan)>("origin_contains_loan_on_entry_op"); + + // .decl loan_live_at(loan, point) + // + // True if the restrictions of the `loan` need to be enforced at `point`. + let loan_live_at = iteration.variable::<((T::Loan, T::Point), ())>("loan_live_at"); + + // .decl live_to_dying_regions(origin1, origin2, point1, point2) + // + // The origins `origin1` and `origin2` are "live to dead" + // on the edge `point1 -> point2` if: + // + // - In `point1`, `origin1` <= `origin2` + // - In `point2`, `origin1` is live but `origin2` is dead. + // + // In that case, `point2` would like to add all the + // live things reachable from `origin2` to `origin1`. + // + let live_to_dying_regions_o2pq = iteration + .variable::<((T::Origin, T::Point, T::Point), T::Origin)>("live_to_dying_regions_o2pq"); + + // .decl dying_region_requires((origin, point1, point2), loan) + // + // The `origin` requires `loan`, but the `origin` goes dead + // along the edge `point1 -> point2`. + let dying_region_requires = iteration + .variable::<((T::Origin, T::Point, T::Point), T::Loan)>("dying_region_requires"); + + // .decl dying_can_reach_origins(origin, point1, point2) + // + // Contains dead origins where we are interested + // in computing the transitive closure of things they + // can reach. + // + // FIXME: this relation was named before renaming the `regions` atoms to `origins`, and + // will need to be renamed to change "_origins" to "_ascendants", "_roots", etc. + let dying_can_reach_origins = + iteration.variable::<((T::Origin, T::Point), T::Point)>("dying_can_reach_origins"); + + // .decl dying_can_reach(origin1, origin2, point1, point2) + // + // Indicates that `origin1`, which is dead + // in `point2`, can reach `origin2` in `point1`. + // + // This is effectively the transitive subset + // relation, but we try to limit it to origins + // that are dying on the edge `point1 -> point2`. + let dying_can_reach_o2q = + iteration.variable::<((T::Origin, T::Point), (T::Origin, T::Point))>("dying_can_reach"); + let dying_can_reach_1 = iteration.variable_indistinct("dying_can_reach_1"); + + // .decl dying_can_reach_live(origin1, origin2, point1, point2) + // + // Indicates that, along the edge `point1 -> point2`, the dead (in `point2`) + // `origin1` can reach the live (in `point2`) `origin2` via a subset + // relation. This is a subset of the full `dying_can_reach` + // relation where we filter down to those cases where `origin2` is + // live in `point2`. + let dying_can_reach_live = iteration + .variable::<((T::Origin, T::Point, T::Point), T::Origin)>("dying_can_reach_live"); + + // .decl dead_borrow_region_can_reach_root((origin, point), loan) + // + // Indicates a "borrow region" `origin` at `point` which is not live on + // entry to `point`. + let dead_borrow_region_can_reach_root = iteration + .variable::<((T::Origin, T::Point), T::Loan)>("dead_borrow_region_can_reach_root"); + + // .decl dead_borrow_region_can_reach_dead((origin2, point), loan) + let dead_borrow_region_can_reach_dead = iteration + .variable::<((T::Origin, T::Point), T::Loan)>("dead_borrow_region_can_reach_dead"); + let dead_borrow_region_can_reach_dead_1 = + iteration.variable_indistinct("dead_borrow_region_can_reach_dead_1"); + + // .decl errors(loan, point) + let errors = iteration.variable("errors"); + let subset_errors = iteration.variable::<(T::Origin, T::Origin, T::Point)>("subset_errors"); + + let subset_placeholder = + iteration.variable::<(T::Origin, T::Origin, T::Point)>("subset_placeholder"); + let subset_placeholder_o2p = iteration.variable_indistinct("subset_placeholder_o2p"); + + // Make "variable" versions of the relations, needed for joins. + loan_issued_at_op.extend( + ctx.loan_issued_at + .iter() + .map(|&(origin, loan, point)| ((origin, point), loan)), + ); + loan_invalidated_at.extend( + ctx.loan_invalidated_at + .iter() + .map(|&(loan, point)| ((loan, point), ())), + ); + origin_live_on_entry_var.extend( + origin_live_on_entry_rel + .iter() + .map(|&(origin, point)| ((origin, point), ())), + ); + + // subset(origin1, origin2, point) :- + // subset_base(origin1, origin2, point). + subset_o1p.extend( + ctx.subset_base + .iter() + .map(|&(origin1, origin2, point)| ((origin1, point), origin2)), + ); + + // origin_contains_loan_on_entry(origin, loan, point) :- + // loan_issued_at(origin, loan, point). + origin_contains_loan_on_entry_op.extend( + ctx.loan_issued_at + .iter() + .map(|&(origin, loan, point)| ((origin, point), loan)), + ); + + // .. and then start iterating rules! + while iteration.changed() { + // Cleanup step: remove symmetries + // - remove origins which are `subset`s of themselves + // + // FIXME: investigate whether is there a better way to do that without complicating + // the rules too much, because it would also require temporary variables and + // impact performance. Until then, the big reduction in tuples improves performance + // a lot, even if we're potentially adding a small number of tuples + // per round just to remove them in the next round. + subset_o1p + .recent + .borrow_mut() + .elements + .retain(|&((origin1, _), origin2)| origin1 != origin2); + + subset_placeholder + .recent + .borrow_mut() + .elements + .retain(|&(origin1, origin2, _)| origin1 != origin2); + subset_placeholder_o2p.from_map(&subset_placeholder, |&(origin1, origin2, point)| { + ((origin2, point), origin1) + }); + + // live_to_dying_regions(origin1, origin2, point1, point2) :- + // subset(origin1, origin2, point1), + // cfg_edge(point1, point2), + // origin_live_on_entry(origin1, point2), + // !origin_live_on_entry(origin2, point2). + live_to_dying_regions_o2pq.from_leapjoin( + &subset_o1p, + ( + cfg_edge_rel.extend_with(|&((_, point1), _)| point1), + origin_live_on_entry_rel.extend_with(|&((origin1, _), _)| origin1), + origin_live_on_entry_rel.extend_anti(|&((_, _), origin2)| origin2), + ), + |&((origin1, point1), origin2), &point2| ((origin2, point1, point2), origin1), + ); + + // dying_region_requires((origin, point1, point2), loan) :- + // origin_contains_loan_on_entry(origin, loan, point1), + // !loan_killed_at(loan, point1), + // cfg_edge(point1, point2), + // !origin_live_on_entry(origin, point2). + dying_region_requires.from_leapjoin( + &origin_contains_loan_on_entry_op, + ( + loan_killed_at.filter_anti(|&((_, point1), loan)| (loan, point1)), + cfg_edge_rel.extend_with(|&((_, point1), _)| point1), + origin_live_on_entry_rel.extend_anti(|&((origin, _), _)| origin), + ), + |&((origin, point1), loan), &point2| ((origin, point1, point2), loan), + ); + + // dying_can_reach_origins(origin2, point1, point2) :- + // live_to_dying_regions(_, origin2, point1, point2). + dying_can_reach_origins.from_map( + &live_to_dying_regions_o2pq, + |&((origin2, point1, point2), _origin1)| ((origin2, point1), point2), + ); + + // dying_can_reach_origins(origin, point1, point2) :- + // dying_region_requires(origin, point1, point2, _loan). + dying_can_reach_origins.from_map( + &dying_region_requires, + |&((origin, point1, point2), _loan)| ((origin, point1), point2), + ); + + // dying_can_reach(origin1, origin2, point1, point2) :- + // dying_can_reach_origins(origin1, point1, point2), + // subset(origin1, origin2, point1). + dying_can_reach_o2q.from_join( + &dying_can_reach_origins, + &subset_o1p, + |&(origin1, point1), &point2, &origin2| ((origin2, point2), (origin1, point1)), + ); + + // dying_can_reach(origin1, origin3, point1, point2) :- + // dying_can_reach(origin1, origin2, point1, point2), + // !origin_live_on_entry(origin2, point2), + // subset(origin2, origin3, point1). + // + // This is the "transitive closure" rule, but + // note that we only apply it with the + // "intermediate" `origin2` is dead at `point2`. + dying_can_reach_1.from_antijoin( + &dying_can_reach_o2q, + &origin_live_on_entry_rel, + |&(origin2, point2), &(origin1, point1)| ((origin2, point1), (origin1, point2)), + ); + dying_can_reach_o2q.from_join( + &dying_can_reach_1, + &subset_o1p, + |&(_origin2, point1), &(origin1, point2), &origin3| { + ((origin3, point2), (origin1, point1)) + }, + ); + + // dying_can_reach_live(origin1, origin2, point1, point2) :- + // dying_can_reach(origin1, origin2, point1, point2), + // origin_live_on_entry(origin2, point2). + dying_can_reach_live.from_join( + &dying_can_reach_o2q, + &origin_live_on_entry_var, + |&(origin2, point2), &(origin1, point1), _| ((origin1, point1, point2), origin2), + ); + + // subset(origin1, origin2, point2) :- + // subset(origin1, origin2, point1), + // cfg_edge(point1, point2), + // origin_live_on_entry(origin1, point2), + // origin_live_on_entry(origin2, point2). + // + // Carry `origin1 <= origin2` from `point1` into `point2` if both `origin1` and + // `origin2` are live in `point2`. + subset_o1p.from_leapjoin( + &subset_o1p, + ( + cfg_edge_rel.extend_with(|&((_, point1), _)| point1), + origin_live_on_entry_rel.extend_with(|&((origin1, _), _)| origin1), + origin_live_on_entry_rel.extend_with(|&((_, _), origin2)| origin2), + ), + |&((origin1, _point1), origin2), &point2| ((origin1, point2), origin2), + ); + + // subset(origin1, origin3, point2) :- + // live_to_dying_regions(origin1, origin2, point1, point2), + // dying_can_reach_live(origin2, origin3, point1, point2). + subset_o1p.from_join( + &live_to_dying_regions_o2pq, + &dying_can_reach_live, + |&(_origin2, _point1, point2), &origin1, &origin3| ((origin1, point2), origin3), + ); + + // origin_contains_loan_on_entry(origin2, loan, point2) :- + // dying_region_requires(origin1, loan, point1, point2), + // dying_can_reach_live(origin1, origin2, point1, point2). + // + // Communicate a `origin1 contains loan` relation across + // an edge `point1 -> point2` where `origin1` is dead in `point2`; in + // that case, for each origin `origin2` live in `point2` + // where `origin1 <= origin2` in `point1`, we add `origin2 contains loan` + // to `point2`. + origin_contains_loan_on_entry_op.from_join( + &dying_region_requires, + &dying_can_reach_live, + |&(_origin1, _point1, point2), &loan, &origin2| ((origin2, point2), loan), + ); + + // origin_contains_loan_on_entry(origin, loan, point2) :- + // origin_contains_loan_on_entry(origin, loan, point1), + // !loan_killed_at(loan, point1), + // cfg_edge(point1, point2), + // origin_live_on_entry(origin, point2). + origin_contains_loan_on_entry_op.from_leapjoin( + &origin_contains_loan_on_entry_op, + ( + loan_killed_at.filter_anti(|&((_, point1), loan)| (loan, point1)), + cfg_edge_rel.extend_with(|&((_, point1), _)| point1), + origin_live_on_entry_rel.extend_with(|&((origin, _), _)| origin), + ), + |&((origin, _), loan), &point2| ((origin, point2), loan), + ); + + // dead_borrow_region_can_reach_root((origin, point), loan) :- + // loan_issued_at(origin, loan, point), + // !origin_live_on_entry(origin, point). + dead_borrow_region_can_reach_root.from_antijoin( + &loan_issued_at_op, + &origin_live_on_entry_rel, + |&(origin, point), &loan| ((origin, point), loan), + ); + + // dead_borrow_region_can_reach_dead((origin, point), loan) :- + // dead_borrow_region_can_reach_root((origin, point), loan). + dead_borrow_region_can_reach_dead + .from_map(&dead_borrow_region_can_reach_root, |&tuple| tuple); + + // dead_borrow_region_can_reach_dead((origin2, point), loan) :- + // dead_borrow_region_can_reach_dead(origin1, loan, point), + // subset(origin1, origin2, point), + // !origin_live_on_entry(origin2, point). + dead_borrow_region_can_reach_dead_1.from_join( + &dead_borrow_region_can_reach_dead, + &subset_o1p, + |&(_origin1, point), &loan, &origin2| ((origin2, point), loan), + ); + dead_borrow_region_can_reach_dead.from_antijoin( + &dead_borrow_region_can_reach_dead_1, + &origin_live_on_entry_rel, + |&(origin2, point), &loan| ((origin2, point), loan), + ); + + // loan_live_at(loan, point) :- + // origin_contains_loan_on_entry(origin, loan, point), + // origin_live_on_entry(origin, point). + loan_live_at.from_join( + &origin_contains_loan_on_entry_op, + &origin_live_on_entry_var, + |&(_origin, point), &loan, _| ((loan, point), ()), + ); + + // loan_live_at(loan, point) :- + // dead_borrow_region_can_reach_dead(origin1, loan, point), + // subset(origin1, origin2, point), + // origin_live_on_entry(origin2, point). + // + // NB: the datafrog code below uses + // `dead_borrow_region_can_reach_dead_1`, which is equal + // to `dead_borrow_region_can_reach_dead` and `subset` + // joined together. + loan_live_at.from_join( + &dead_borrow_region_can_reach_dead_1, + &origin_live_on_entry_var, + |&(_origin2, point), &loan, _| ((loan, point), ()), + ); + + // errors(loan, point) :- + // loan_invalidated_at(loan, point), + // loan_live_at(loan, point). + errors.from_join( + &loan_invalidated_at, + &loan_live_at, + |&(loan, point), _, _| (loan, point), + ); + + // subset_placeholder(Origin1, Origin2, Point) :- + // subset(Origin1, Origin2, Point), + // placeholder_origin(Origin1). + subset_placeholder.from_leapjoin( + &subset_o1p, + ( + placeholder_origin.extend_with(|&((origin1, _point), _origin2)| origin1), + // remove symmetries: + datafrog::ValueFilter::from(|&((origin1, _point), origin2), _| { + origin1 != origin2 + }), + ), + |&((origin1, point), origin2), _| (origin1, origin2, point), + ); + + // We compute the transitive closure of the placeholder origins, so we + // maintain the invariant from the rule above that `Origin1` is a placeholder origin. + // + // subset_placeholder(Origin1, Origin3, Point) :- + // subset_placeholder(Origin1, Origin2, Point), + // subset(Origin2, Origin3, Point). + subset_placeholder.from_join( + &subset_placeholder_o2p, + &subset_o1p, + |&(_origin2, point), &origin1, &origin3| (origin1, origin3, point), + ); + + // subset_error(Origin1, Origin2, Point) :- + // subset_placeholder(Origin1, Origin2, Point), + // placeholder_origin(Origin2), + // !known_placeholder_subset(Origin1, Origin2). + subset_errors.from_leapjoin( + &subset_placeholder, + ( + placeholder_origin.extend_with(|&(_origin1, origin2, _point)| origin2), + known_placeholder_subset + .filter_anti(|&(origin1, origin2, _point)| (origin1, origin2)), + // remove symmetries: + datafrog::ValueFilter::from(|&(origin1, origin2, _point), _| { + origin1 != origin2 + }), + ), + |&(origin1, origin2, point), _| (origin1, origin2, point), + ); + } + + if result.dump_enabled { + let subset_o1p = subset_o1p.complete(); + assert!( + subset_o1p + .iter() + .filter(|&((origin1, _), origin2)| origin1 == origin2) + .count() + == 0, + "unwanted subset symmetries" + ); + for &((origin1, location), origin2) in subset_o1p.iter() { + result + .subset + .entry(location) + .or_default() + .entry(origin1) + .or_default() + .insert(origin2); + } + + let origin_contains_loan_on_entry_op = origin_contains_loan_on_entry_op.complete(); + for &((origin, location), loan) in origin_contains_loan_on_entry_op.iter() { + result + .origin_contains_loan_at + .entry(location) + .or_default() + .entry(origin) + .or_default() + .insert(loan); + } + + let loan_live_at = loan_live_at.complete(); + for &((loan, location), _) in loan_live_at.iter() { + result.loan_live_at.entry(location).or_default().push(loan); + } + } + + (errors.complete(), subset_errors.complete()) + }; + + info!( + "analysis done: {} `errors` tuples, {} `subset_errors` tuples, {:?}", + errors.len(), + subset_errors.len(), + timer.elapsed() + ); + + (errors, subset_errors) +} diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/polonius-engine/src/output/initialization.rs b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/polonius-engine/src/output/initialization.rs new file mode 100644 index 0000000..30409d9 --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/polonius-engine/src/output/initialization.rs @@ -0,0 +1,284 @@ +use std::time::Instant; + +use crate::facts::FactTypes; +use crate::output::{InitializationContext, Output}; + +use datafrog::{Iteration, Relation, RelationLeaper}; + +// This represents the output of an intermediate elaboration step (step 1). +struct TransitivePaths<T: FactTypes> { + path_moved_at: Relation<(T::Path, T::Point)>, + path_assigned_at: Relation<(T::Path, T::Point)>, + path_accessed_at: Relation<(T::Path, T::Point)>, + path_begins_with_var: Relation<(T::Path, T::Variable)>, +} + +struct InitializationStatus<T: FactTypes> { + var_maybe_partly_initialized_on_exit: Relation<(T::Variable, T::Point)>, + move_error: Relation<(T::Path, T::Point)>, +} + +pub(super) struct InitializationResult<T: FactTypes>( + pub(super) Relation<(T::Variable, T::Point)>, + pub(super) Relation<(T::Path, T::Point)>, +); + +// Step 1: compute transitive closures of path operations. This would elaborate, +// for example, an access to x into an access to x.f, x.f.0, etc. We do this for: +// - access to a path +// - initialization of a path +// - moves of a path +// FIXME: transitive rooting in a variable (path_begins_with_var) +// Note that this step may not be entirely necessary! +fn compute_transitive_paths<T: FactTypes>( + child_path: Vec<(T::Path, T::Path)>, + path_assigned_at_base: Vec<(T::Path, T::Point)>, + path_moved_at_base: Vec<(T::Path, T::Point)>, + path_accessed_at_base: Vec<(T::Path, T::Point)>, + path_is_var: Vec<(T::Path, T::Variable)>, +) -> TransitivePaths<T> { + let mut iteration = Iteration::new(); + let child_path: Relation<(T::Path, T::Path)> = child_path.into(); + + let ancestor_path = iteration.variable::<(T::Path, T::Path)>("ancestor"); + + // These are the actual targets: + let path_moved_at = iteration.variable::<(T::Path, T::Point)>("path_moved_at"); + let path_assigned_at = iteration.variable::<(T::Path, T::Point)>("path_initialized_at"); + let path_accessed_at = iteration.variable::<(T::Path, T::Point)>("path_accessed_at"); + let path_begins_with_var = iteration.variable::<(T::Path, T::Variable)>("path_begins_with_var"); + + // ancestor_path(Parent, Child) :- child_path(Child, Parent). + ancestor_path.extend(child_path.iter().map(|&(child, parent)| (parent, child))); + + // path_moved_at(Path, Point) :- path_moved_at_base(Path, Point). + path_moved_at.insert(path_moved_at_base.into()); + + // path_assigned_at(Path, Point) :- path_assigned_at_base(Path, Point). + path_assigned_at.insert(path_assigned_at_base.into()); + + // path_accessed_at(Path, Point) :- path_accessed_at_base(Path, Point). + path_accessed_at.insert(path_accessed_at_base.into()); + + // path_begins_with_var(Path, Var) :- path_is_var(Path, Var). + path_begins_with_var.insert(path_is_var.into()); + + while iteration.changed() { + // ancestor_path(Grandparent, Child) :- + // ancestor_path(Parent, Child), + // child_path(Parent, Grandparent). + ancestor_path.from_join( + &ancestor_path, + &child_path, + |&_parent, &child, &grandparent| (grandparent, child), + ); + + // moving a path moves its children + // path_moved_at(Child, Point) :- + // path_moved_at(Parent, Point), + // ancestor_path(Parent, Child). + path_moved_at.from_join(&path_moved_at, &ancestor_path, |&_parent, &p, &child| { + (child, p) + }); + + // initialising x at p initialises all x:s children + // path_assigned_at(Child, point) :- + // path_assigned_at(Parent, point), + // ancestor_path(Parent, Child). + path_assigned_at.from_join(&path_assigned_at, &ancestor_path, |&_parent, &p, &child| { + (child, p) + }); + + // accessing x at p accesses all x:s children at p (actually, + // accesses should be maximally precise and this shouldn't happen?) + // path_accessed_at(Child, point) :- + // path_accessed_at(Parent, point), + // ancestor_path(Parent, Child). + path_accessed_at.from_join(&path_accessed_at, &ancestor_path, |&_parent, &p, &child| { + (child, p) + }); + + // path_begins_with_var(Child, Var) :- + // path_begins_with_var(Parent, Var) + // ancestor_path(Parent, Child). + path_begins_with_var.from_join( + &path_begins_with_var, + &ancestor_path, + |&_parent, &var, &child| (child, var), + ); + } + + TransitivePaths { + path_assigned_at: path_assigned_at.complete(), + path_moved_at: path_moved_at.complete(), + path_accessed_at: path_accessed_at.complete(), + path_begins_with_var: path_begins_with_var.complete(), + } +} + +// Step 2: Compute path initialization and deinitialization across the CFG. +fn compute_move_errors<T: FactTypes>( + ctx: TransitivePaths<T>, + cfg_edge: &Relation<(T::Point, T::Point)>, + output: &mut Output<T>, +) -> InitializationStatus<T> { + let mut iteration = Iteration::new(); + // Variables + + // var_maybe_partly_initialized_on_exit(var, point): Upon leaving `point`, + // `var` is partially initialized for some path through the CFG, that is + // there has been an initialization of var, and var has not been moved in + // all paths through the CFG. + let var_maybe_partly_initialized_on_exit = + iteration.variable::<(T::Variable, T::Point)>("var_maybe_partly_initialized_on_exit"); + + // path_maybe_initialized_on_exit(path, point): Upon leaving `point`, the + // move path `path` is initialized for some path through the CFG. + let path_maybe_initialized_on_exit = + iteration.variable::<(T::Path, T::Point)>("path_maybe_initialized_on_exit"); + + // path_maybe_uninitialized_on_exit(Path, Point): There exists at least one + // path through the CFG to Point such that `Path` has been moved out by the + // time we arrive at `Point` without it being re-initialized for sure. + let path_maybe_uninitialized_on_exit = + iteration.variable::<(T::Path, T::Point)>("path_maybe_uninitialized_on_exit"); + + // move_error(Path, Point): There is an access to `Path` at `Point`, but + // `Path` is potentially moved (or never initialised). + let move_error = iteration.variable::<(T::Path, T::Point)>("move_error"); + + // Initial propagation of static relations + + // path_maybe_initialized_on_exit(path, point) :- path_assigned_at(path, point). + path_maybe_initialized_on_exit.insert(ctx.path_assigned_at.clone()); + + // path_maybe_uninitialized_on_exit(path, point) :- path_moved_at(path, point). + path_maybe_uninitialized_on_exit.insert(ctx.path_moved_at.clone()); + + while iteration.changed() { + // path_maybe_initialized_on_exit(path, point2) :- + // path_maybe_initialized_on_exit(path, point1), + // cfg_edge(point1, point2), + // !path_moved_at(path, point2). + path_maybe_initialized_on_exit.from_leapjoin( + &path_maybe_initialized_on_exit, + ( + cfg_edge.extend_with(|&(_path, point1)| point1), + ctx.path_moved_at.extend_anti(|&(path, _point1)| path), + ), + |&(path, _point1), &point2| (path, point2), + ); + + // path_maybe_uninitialized_on_exit(path, point2) :- + // path_maybe_uninitialized_on_exit(path, point1), + // cfg_edge(point1, point2) + // !path_assigned_at(path, point2). + path_maybe_uninitialized_on_exit.from_leapjoin( + &path_maybe_uninitialized_on_exit, + ( + cfg_edge.extend_with(|&(_path, point1)| point1), + ctx.path_assigned_at.extend_anti(|&(path, _point1)| path), + ), + |&(path, _point1), &point2| (path, point2), + ); + + // var_maybe_partly_initialized_on_exit(var, point) :- + // path_maybe_initialized_on_exit(path, point). + // path_begins_with_var(path, var). + var_maybe_partly_initialized_on_exit.from_leapjoin( + &path_maybe_initialized_on_exit, + ctx.path_begins_with_var.extend_with(|&(path, _point)| path), + |&(_path, point), &var| (var, point), + ); + + // move_error(Path, TargetNode) :- + // path_maybe_uninitialized_on_exit(Path, SourceNode), + // cfg_edge(SourceNode, TargetNode), + // path_accessed_at(Path, TargetNode). + move_error.from_leapjoin( + &path_maybe_uninitialized_on_exit, + ( + cfg_edge.extend_with(|&(_path, source_node)| source_node), + ctx.path_accessed_at + .extend_with(|&(path, _source_node)| path), + ), + |&(path, _source_node), &target_node| (path, target_node), + ); + } + + if output.dump_enabled { + for &(path, location) in path_maybe_initialized_on_exit.complete().iter() { + output + .path_maybe_initialized_on_exit + .entry(location) + .or_default() + .push(path); + } + + for &(path, location) in path_maybe_uninitialized_on_exit.complete().iter() { + output + .path_maybe_uninitialized_on_exit + .entry(location) + .or_default() + .push(path); + } + } + + InitializationStatus { + var_maybe_partly_initialized_on_exit: var_maybe_partly_initialized_on_exit.complete(), + move_error: move_error.complete(), + } +} + +// Compute two things: +// +// - an over-approximation of the initialization of variables. This is used in +// the origin_live_on_entry computations to determine when a drop may happen; a +// definitely moved variable would not be actually dropped. +// - move errors. +// +// The process is split into two stages: +// +// 1. Compute the transitive closure of path accesses. That is, accessing `f.a` +// would access `f.a.b`, etc. +// 2. Use this to compute both paths that may be initialized and paths that may +// have been deinitialized, which in turn can be used to find move errors (an +// access to a path that may be deinitialized). +pub(super) fn compute<T: FactTypes>( + ctx: InitializationContext<T>, + cfg_edge: &Relation<(T::Point, T::Point)>, + output: &mut Output<T>, +) -> InitializationResult<T> { + let timer = Instant::now(); + + let transitive_paths = compute_transitive_paths::<T>( + ctx.child_path, + ctx.path_assigned_at_base, + ctx.path_moved_at_base, + ctx.path_accessed_at_base, + ctx.path_is_var, + ); + info!("initialization phase 1 completed: {:?}", timer.elapsed()); + + let InitializationStatus { + var_maybe_partly_initialized_on_exit, + move_error, + } = compute_move_errors::<T>(transitive_paths, cfg_edge, output); + info!( + "initialization phase 2: {} move errors in {:?}", + move_error.elements.len(), + timer.elapsed() + ); + + if output.dump_enabled { + for &(var, location) in var_maybe_partly_initialized_on_exit.iter() { + output + .var_maybe_partly_initialized_on_exit + .entry(location) + .or_default() + .push(var); + } + } + + InitializationResult(var_maybe_partly_initialized_on_exit, move_error) +} diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/polonius-engine/src/output/liveness.rs b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/polonius-engine/src/output/liveness.rs new file mode 100644 index 0000000..1b4b4ce --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/polonius-engine/src/output/liveness.rs @@ -0,0 +1,170 @@ +// Copyright 2019 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! An implementation of the origin liveness calculation logic + +use std::collections::BTreeSet; +use std::time::Instant; + +use crate::facts::FactTypes; +use crate::output::{LivenessContext, Output}; + +use datafrog::{Iteration, Relation, RelationLeaper}; + +pub(super) fn compute_live_origins<T: FactTypes>( + ctx: LivenessContext<T>, + cfg_edge: &Relation<(T::Point, T::Point)>, + var_maybe_partly_initialized_on_exit: Relation<(T::Variable, T::Point)>, + output: &mut Output<T>, +) -> Vec<(T::Origin, T::Point)> { + let timer = Instant::now(); + let mut iteration = Iteration::new(); + + // Relations + let var_defined_at: Relation<(T::Variable, T::Point)> = ctx.var_defined_at.into(); + let cfg_edge_reverse: Relation<(T::Point, T::Point)> = cfg_edge + .iter() + .map(|&(point1, point2)| (point2, point1)) + .collect(); + let use_of_var_derefs_origin: Relation<(T::Variable, T::Origin)> = + ctx.use_of_var_derefs_origin.into(); + let drop_of_var_derefs_origin: Relation<(T::Variable, T::Origin)> = + ctx.drop_of_var_derefs_origin.into(); + let var_dropped_at: Relation<((T::Variable, T::Point), ())> = ctx + .var_dropped_at + .into_iter() + .map(|(var, point)| ((var, point), ())) + .collect(); + + // Variables + + // `var_live_on_entry`: variable `var` is live upon entry at `point` + let var_live_on_entry = iteration.variable::<(T::Variable, T::Point)>("var_live_on_entry"); + // `var_drop_live_on_entry`: variable `var` is drop-live (will be used for a drop) upon entry in `point` + let var_drop_live_on_entry = + iteration.variable::<(T::Variable, T::Point)>("var_drop_live_on_entry"); + + // This is what we are actually calculating: + let origin_live_on_entry = iteration.variable::<(T::Origin, T::Point)>("origin_live_on_entry"); + + // This propagates the relation `var_live_on_entry(var, point) :- var_used_at(var, point)`: + var_live_on_entry.insert(ctx.var_used_at.into()); + + // var_maybe_partly_initialized_on_entry(var, point2) :- + // var_maybe_partly_initialized_on_exit(var, point1), + // cfg_edge(point1, point2). + let var_maybe_partly_initialized_on_entry = Relation::from_leapjoin( + &var_maybe_partly_initialized_on_exit, + cfg_edge.extend_with(|&(_var, point1)| point1), + |&(var, _point1), &point2| ((var, point2), ()), + ); + + // var_drop_live_on_entry(var, point) :- + // var_dropped_at(var, point), + // var_maybe_partly_initialized_on_entry(var, point). + var_drop_live_on_entry.insert(Relation::from_join( + &var_dropped_at, + &var_maybe_partly_initialized_on_entry, + |&(var, point), _, _| (var, point), + )); + + while iteration.changed() { + // origin_live_on_entry(origin, point) :- + // var_drop_live_on_entry(var, point), + // drop_of_var_derefs_origin(var, origin). + origin_live_on_entry.from_join( + &var_drop_live_on_entry, + &drop_of_var_derefs_origin, + |_var, &point, &origin| (origin, point), + ); + + // origin_live_on_entry(origin, point) :- + // var_live_on_entry(var, point), + // use_of_var_derefs_origin(var, origin). + origin_live_on_entry.from_join( + &var_live_on_entry, + &use_of_var_derefs_origin, + |_var, &point, &origin| (origin, point), + ); + + // var_live_on_entry(var, point1) :- + // var_live_on_entry(var, point2), + // cfg_edge(point1, point2), + // !var_defined(var, point1). + var_live_on_entry.from_leapjoin( + &var_live_on_entry, + ( + var_defined_at.extend_anti(|&(var, _point2)| var), + cfg_edge_reverse.extend_with(|&(_var, point2)| point2), + ), + |&(var, _point2), &point1| (var, point1), + ); + + // var_drop_live_on_entry(Var, SourceNode) :- + // var_drop_live_on_entry(Var, TargetNode), + // cfg_edge(SourceNode, TargetNode), + // !var_defined_at(Var, SourceNode), + // var_maybe_partly_initialized_on_exit(Var, SourceNode). + var_drop_live_on_entry.from_leapjoin( + &var_drop_live_on_entry, + ( + var_defined_at.extend_anti(|&(var, _target_node)| var), + cfg_edge_reverse.extend_with(|&(_var, target_node)| target_node), + var_maybe_partly_initialized_on_exit.extend_with(|&(var, _target_node)| var), + ), + |&(var, _targetnode), &source_node| (var, source_node), + ); + } + + let origin_live_on_entry = origin_live_on_entry.complete(); + + info!( + "compute_live_origins() completed: {} tuples, {:?}", + origin_live_on_entry.len(), + timer.elapsed(), + ); + + if output.dump_enabled { + let var_drop_live_on_entry = var_drop_live_on_entry.complete(); + for &(var, location) in var_drop_live_on_entry.iter() { + output + .var_drop_live_on_entry + .entry(location) + .or_default() + .push(var); + } + + let var_live_on_entry = var_live_on_entry.complete(); + for &(var, location) in var_live_on_entry.iter() { + output + .var_live_on_entry + .entry(location) + .or_default() + .push(var); + } + } + + origin_live_on_entry.elements +} + +pub(super) fn make_universal_regions_live<T: FactTypes>( + origin_live_on_entry: &mut Vec<(T::Origin, T::Point)>, + cfg_node: &BTreeSet<T::Point>, + universal_regions: &[T::Origin], +) { + debug!("make_universal_regions_live()"); + + origin_live_on_entry.reserve(universal_regions.len() * cfg_node.len()); + for &origin in universal_regions.iter() { + for &point in cfg_node.iter() { + origin_live_on_entry.push((origin, point)); + } + } +} diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/polonius-engine/src/output/location_insensitive.rs b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/polonius-engine/src/output/location_insensitive.rs new file mode 100644 index 0000000..83ce277 --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/polonius-engine/src/output/location_insensitive.rs @@ -0,0 +1,156 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use datafrog::{Iteration, Relation, RelationLeaper}; +use std::time::Instant; + +use crate::facts::FactTypes; +use crate::output::{Context, Output}; + +pub(super) fn compute<T: FactTypes>( + ctx: &Context<'_, T>, + result: &mut Output<T>, +) -> ( + Relation<(T::Loan, T::Point)>, + Relation<(T::Origin, T::Origin)>, +) { + let timer = Instant::now(); + + let (potential_errors, potential_subset_errors) = { + // Static inputs + let origin_live_on_entry = &ctx.origin_live_on_entry; + let loan_invalidated_at = &ctx.loan_invalidated_at; + let placeholder_origin = &ctx.placeholder_origin; + let placeholder_loan = &ctx.placeholder_loan; + let known_contains = &ctx.known_contains; + + // subset(Origin1, Origin2) :- + // subset_base(Origin1, Origin2, _). + let subset = Relation::from_iter( + ctx.subset_base + .iter() + .map(|&(origin1, origin2, _point)| (origin1, origin2)), + ); + + // Create a new iteration context, ... + let mut iteration = Iteration::new(); + + // .. some variables, .. + let origin_contains_loan_on_entry = + iteration.variable::<(T::Origin, T::Loan)>("origin_contains_loan_on_entry"); + + let potential_errors = iteration.variable::<(T::Loan, T::Point)>("potential_errors"); + let potential_subset_errors = + iteration.variable::<(T::Origin, T::Origin)>("potential_subset_errors"); + + // load initial facts. + + // origin_contains_loan_on_entry(Origin, Loan) :- + // loan_issued_at(Origin, Loan, _). + origin_contains_loan_on_entry.extend( + ctx.loan_issued_at + .iter() + .map(|&(origin, loan, _point)| (origin, loan)), + ); + + // origin_contains_loan_on_entry(Origin, Loan) :- + // placeholder_loan(Origin, Loan). + origin_contains_loan_on_entry.extend( + placeholder_loan + .iter() + .map(|&(loan, origin)| (origin, loan)), + ); + + // .. and then start iterating rules! + while iteration.changed() { + // origin_contains_loan_on_entry(Origin2, Loan) :- + // origin_contains_loan_on_entry(Origin1, Loan), + // subset(Origin1, Origin2). + // + // Note: Since `subset` is effectively a static input, this join can be ported to + // a leapjoin. Doing so, however, was 7% slower on `clap`. + origin_contains_loan_on_entry.from_join( + &origin_contains_loan_on_entry, + &subset, + |&_origin1, &loan, &origin2| (origin2, loan), + ); + + // loan_live_at(Loan, Point) :- + // origin_contains_loan_on_entry(Origin, Loan), + // origin_live_on_entry(Origin, Point) + // + // potential_errors(Loan, Point) :- + // loan_invalidated_at(Loan, Point), + // loan_live_at(Loan, Point). + // + // Note: we don't need to materialize `loan_live_at` here + // so we can inline it in the `potential_errors` relation. + // + potential_errors.from_leapjoin( + &origin_contains_loan_on_entry, + ( + origin_live_on_entry.extend_with(|&(origin, _loan)| origin), + loan_invalidated_at.extend_with(|&(_origin, loan)| loan), + ), + |&(_origin, loan), &point| (loan, point), + ); + + // potential_subset_errors(Origin1, Origin2) :- + // placeholder(Origin1, Loan1), + // placeholder(Origin2, _), + // origin_contains_loan_on_entry(Origin2, Loan1), + // !known_contains(Origin2, Loan1). + potential_subset_errors.from_leapjoin( + &origin_contains_loan_on_entry, + ( + known_contains.filter_anti(|&(origin2, loan1)| (origin2, loan1)), + placeholder_origin.filter_with(|&(origin2, _loan1)| (origin2, ())), + placeholder_loan.extend_with(|&(_origin2, loan1)| loan1), + // remove symmetries: + datafrog::ValueFilter::from(|&(origin2, _loan1), &origin1| origin2 != origin1), + ), + |&(origin2, _loan1), &origin1| (origin1, origin2), + ); + } + + if result.dump_enabled { + for &(origin1, origin2) in subset.iter() { + result + .subset_anywhere + .entry(origin1) + .or_default() + .insert(origin2); + } + + let origin_contains_loan_on_entry = origin_contains_loan_on_entry.complete(); + for &(origin, loan) in origin_contains_loan_on_entry.iter() { + result + .origin_contains_loan_anywhere + .entry(origin) + .or_default() + .insert(loan); + } + } + + ( + potential_errors.complete(), + potential_subset_errors.complete(), + ) + }; + + info!( + "analysis done: {} `potential_errors` tuples, {} `potential_subset_errors` tuples, {:?}", + potential_errors.len(), + potential_subset_errors.len(), + timer.elapsed() + ); + + (potential_errors, potential_subset_errors) +} diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/polonius-engine/src/output/mod.rs b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/polonius-engine/src/output/mod.rs new file mode 100644 index 0000000..b840e4b --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/polonius-engine/src/output/mod.rs @@ -0,0 +1,614 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use datafrog::Relation; +use rustc_hash::{FxHashMap, FxHashSet}; +use std::borrow::Cow; +use std::collections::{BTreeMap, BTreeSet}; + +use crate::facts::{AllFacts, Atom, FactTypes}; + +mod datafrog_opt; +mod initialization; +mod liveness; +mod location_insensitive; +mod naive; + +#[derive(Debug, Clone, Copy)] +pub enum Algorithm { + /// Simple rules, but slower to execute + Naive, + + /// Optimized variant of the rules + DatafrogOpt, + + /// Fast to compute, but imprecise: there can be false-positives + /// but no false-negatives. Tailored for quick "early return" situations. + LocationInsensitive, + + /// Compares the `Naive` and `DatafrogOpt` variants to ensure they indeed + /// compute the same errors. + Compare, + + /// Combination of the fast `LocationInsensitive` pre-pass, followed by + /// the more expensive `DatafrogOpt` variant. + Hybrid, +} + +impl Algorithm { + /// Optimized variants that ought to be equivalent to "naive" + pub const OPTIMIZED: &'static [Algorithm] = &[Algorithm::DatafrogOpt]; + + pub fn variants() -> [&'static str; 5] { + [ + "Naive", + "DatafrogOpt", + "LocationInsensitive", + "Compare", + "Hybrid", + ] + } +} + +impl ::std::str::FromStr for Algorithm { + type Err = String; + fn from_str(s: &str) -> Result<Self, Self::Err> { + match s.to_lowercase().as_ref() { + "naive" => Ok(Algorithm::Naive), + "datafrogopt" => Ok(Algorithm::DatafrogOpt), + "locationinsensitive" => Ok(Algorithm::LocationInsensitive), + "compare" => Ok(Algorithm::Compare), + "hybrid" => Ok(Algorithm::Hybrid), + _ => Err(String::from( + "valid values: Naive, DatafrogOpt, LocationInsensitive, Compare, Hybrid", + )), + } + } +} + +#[derive(Clone, Debug)] +pub struct Output<T: FactTypes> { + pub errors: FxHashMap<T::Point, Vec<T::Loan>>, + pub subset_errors: FxHashMap<T::Point, BTreeSet<(T::Origin, T::Origin)>>, + pub move_errors: FxHashMap<T::Point, Vec<T::Path>>, + + pub dump_enabled: bool, + + // these are just for debugging + pub loan_live_at: FxHashMap<T::Point, Vec<T::Loan>>, + pub origin_contains_loan_at: FxHashMap<T::Point, BTreeMap<T::Origin, BTreeSet<T::Loan>>>, + pub origin_contains_loan_anywhere: FxHashMap<T::Origin, BTreeSet<T::Loan>>, + pub origin_live_on_entry: FxHashMap<T::Point, Vec<T::Origin>>, + pub loan_invalidated_at: FxHashMap<T::Point, Vec<T::Loan>>, + pub subset: FxHashMap<T::Point, BTreeMap<T::Origin, BTreeSet<T::Origin>>>, + pub subset_anywhere: FxHashMap<T::Origin, BTreeSet<T::Origin>>, + pub var_live_on_entry: FxHashMap<T::Point, Vec<T::Variable>>, + pub var_drop_live_on_entry: FxHashMap<T::Point, Vec<T::Variable>>, + pub path_maybe_initialized_on_exit: FxHashMap<T::Point, Vec<T::Path>>, + pub path_maybe_uninitialized_on_exit: FxHashMap<T::Point, Vec<T::Path>>, + pub known_contains: FxHashMap<T::Origin, BTreeSet<T::Loan>>, + pub var_maybe_partly_initialized_on_exit: FxHashMap<T::Point, Vec<T::Variable>>, +} + +/// Subset of `AllFacts` dedicated to initialization +struct InitializationContext<T: FactTypes> { + child_path: Vec<(T::Path, T::Path)>, + path_is_var: Vec<(T::Path, T::Variable)>, + path_assigned_at_base: Vec<(T::Path, T::Point)>, + path_moved_at_base: Vec<(T::Path, T::Point)>, + path_accessed_at_base: Vec<(T::Path, T::Point)>, +} + +/// Subset of `AllFacts` dedicated to liveness +struct LivenessContext<T: FactTypes> { + var_used_at: Vec<(T::Variable, T::Point)>, + var_defined_at: Vec<(T::Variable, T::Point)>, + var_dropped_at: Vec<(T::Variable, T::Point)>, + use_of_var_derefs_origin: Vec<(T::Variable, T::Origin)>, + drop_of_var_derefs_origin: Vec<(T::Variable, T::Origin)>, +} + +/// Subset of `AllFacts` dedicated to borrow checking, and data ready to use by the variants +struct Context<'ctx, T: FactTypes> { + // `Relation`s used as static inputs, by all variants + origin_live_on_entry: Relation<(T::Origin, T::Point)>, + loan_invalidated_at: Relation<(T::Loan, T::Point)>, + + // static inputs used via `Variable`s, by all variants + subset_base: &'ctx Vec<(T::Origin, T::Origin, T::Point)>, + loan_issued_at: &'ctx Vec<(T::Origin, T::Loan, T::Point)>, + + // static inputs used by variants other than `LocationInsensitive` + loan_killed_at: Relation<(T::Loan, T::Point)>, + known_contains: Relation<(T::Origin, T::Loan)>, + placeholder_origin: Relation<(T::Origin, ())>, + placeholder_loan: Relation<(T::Loan, T::Origin)>, + + // The `known_placeholder_subset` relation in the facts does not necessarily contain all the + // transitive subsets. The transitive closure is always needed, so this version here is fully + // closed over. + known_placeholder_subset: Relation<(T::Origin, T::Origin)>, + + // while this static input is unused by `LocationInsensitive`, it's depended on by + // initialization and liveness, so already computed by the time we get to borrowcking. + cfg_edge: Relation<(T::Point, T::Point)>, + + // Partial results possibly used by other variants as input. Not currently used yet. + #[allow(dead_code)] + potential_errors: Option<FxHashSet<T::Loan>>, + #[allow(dead_code)] + potential_subset_errors: Option<Relation<(T::Origin, T::Origin)>>, +} + +impl<T: FactTypes> Output<T> { + /// All variants require the same initial preparations, done in multiple + /// successive steps: + /// - compute initialization data + /// - compute liveness + /// - prepare static inputs as shared `Relation`s + /// - in cases where `LocationInsensitive` variant is ran as a filtering pre-pass, + /// partial results can also be stored in the context, so that the following + /// variant can use it to prune its own input data + pub fn compute(all_facts: &AllFacts<T>, algorithm: Algorithm, dump_enabled: bool) -> Self { + let mut result = Output::new(dump_enabled); + + // TODO: remove all the cloning thereafter, but that needs to be done in concert with rustc + + let cfg_edge = all_facts.cfg_edge.clone().into(); + + // 1) Initialization + let initialization_ctx = InitializationContext { + child_path: all_facts.child_path.clone(), + path_is_var: all_facts.path_is_var.clone(), + path_assigned_at_base: all_facts.path_assigned_at_base.clone(), + path_moved_at_base: all_facts.path_moved_at_base.clone(), + path_accessed_at_base: all_facts.path_accessed_at_base.clone(), + }; + + let initialization::InitializationResult::<T>( + var_maybe_partly_initialized_on_exit, + move_errors, + ) = initialization::compute(initialization_ctx, &cfg_edge, &mut result); + + // FIXME: move errors should prevent the computation from continuing: we can't compute + // liveness and analyze loans accurately when there are move errors, and should early + // return here. + for &(path, location) in move_errors.iter() { + result.move_errors.entry(location).or_default().push(path); + } + + // 2) Liveness + let liveness_ctx = LivenessContext { + var_used_at: all_facts.var_used_at.clone(), + var_defined_at: all_facts.var_defined_at.clone(), + var_dropped_at: all_facts.var_dropped_at.clone(), + use_of_var_derefs_origin: all_facts.use_of_var_derefs_origin.clone(), + drop_of_var_derefs_origin: all_facts.drop_of_var_derefs_origin.clone(), + }; + + let mut origin_live_on_entry = liveness::compute_live_origins( + liveness_ctx, + &cfg_edge, + var_maybe_partly_initialized_on_exit, + &mut result, + ); + + let cfg_node = cfg_edge + .iter() + .map(|&(point1, _)| point1) + .chain(cfg_edge.iter().map(|&(_, point2)| point2)) + .collect(); + + liveness::make_universal_regions_live::<T>( + &mut origin_live_on_entry, + &cfg_node, + &all_facts.universal_region, + ); + + // 3) Borrow checking + + // Prepare data as datafrog relations, ready to join. + // + // Note: if rustc and polonius had more interaction, we could also delay or avoid + // generating some of the facts that are now always present here. For example, + // the `LocationInsensitive` variant doesn't use the `loan_killed_at` relation, so we could + // technically delay computing and passing it from rustc, when using this or the `Hybrid` + // variants, to after the pre-pass has made sure we actually need to compute the full + // analysis. If these facts happened to be recorded in separate MIR walks, we might also + // avoid generating those facts. + + let origin_live_on_entry = origin_live_on_entry.into(); + + // TODO: also flip the order of this relation's arguments in rustc + // from `loan_invalidated_at(point, loan)` to `loan_invalidated_at(loan, point)`. + // to avoid this allocation. + let loan_invalidated_at = Relation::from_iter( + all_facts + .loan_invalidated_at + .iter() + .map(|&(point, loan)| (loan, point)), + ); + + let loan_killed_at = all_facts.loan_killed_at.clone().into(); + + // `known_placeholder_subset` is a list of all the `'a: 'b` subset relations the user gave: + // it's not required to be transitive. `known_contains` is its transitive closure: a list + // of all the known placeholder loans that each of these placeholder origins contains. + // Given the `known_placeholder_subset`s `'a: 'b` and `'b: 'c`: in the `known_contains` + // relation, `'a` will also contain `'c`'s placeholder loan. + let known_placeholder_subset = all_facts.known_placeholder_subset.clone().into(); + let known_contains = + Output::<T>::compute_known_contains(&known_placeholder_subset, &all_facts.placeholder); + + // Fully close over the `known_placeholder_subset` relation. + let known_placeholder_subset = + Output::<T>::compute_known_placeholder_subset(&known_placeholder_subset); + + let placeholder_origin: Relation<_> = Relation::from_iter( + all_facts + .universal_region + .iter() + .map(|&origin| (origin, ())), + ); + + let placeholder_loan = Relation::from_iter( + all_facts + .placeholder + .iter() + .map(|&(origin, loan)| (loan, origin)), + ); + + // Ask the variants to compute errors in their own way + let mut ctx = Context { + origin_live_on_entry, + loan_invalidated_at, + cfg_edge, + subset_base: &all_facts.subset_base, + loan_issued_at: &all_facts.loan_issued_at, + loan_killed_at, + known_contains, + known_placeholder_subset, + placeholder_origin, + placeholder_loan, + potential_errors: None, + potential_subset_errors: None, + }; + + let (errors, subset_errors) = match algorithm { + Algorithm::LocationInsensitive => { + let (potential_errors, potential_subset_errors) = + location_insensitive::compute(&ctx, &mut result); + + // Note: the error location is meaningless for a location-insensitive + // subset error analysis. This is acceptable here as this variant is not one + // which should be used directly besides debugging, the `Hybrid` variant will + // take advantage of its result. + let potential_subset_errors: Relation<(T::Origin, T::Origin, T::Point)> = + Relation::from_iter( + potential_subset_errors + .into_iter() + .map(|&(origin1, origin2)| (origin1, origin2, 0.into())), + ); + + (potential_errors, potential_subset_errors) + } + Algorithm::Naive => naive::compute(&ctx, &mut result), + Algorithm::DatafrogOpt => datafrog_opt::compute(&ctx, &mut result), + Algorithm::Hybrid => { + // Execute the fast `LocationInsensitive` computation as a pre-pass: + // if it finds no possible errors, we don't need to do the more complex + // computations as they won't find errors either, and we can return early. + let (potential_errors, potential_subset_errors) = + location_insensitive::compute(&ctx, &mut result); + + if potential_errors.is_empty() && potential_subset_errors.is_empty() { + // There are no loan errors, nor subset errors, we can early return + // empty errors lists and avoid doing the heavy analysis. + (potential_errors, Vec::new().into()) + } else { + // Record these potential errors as they can be used to limit the next + // variant's work to only these loans. + ctx.potential_errors = + Some(potential_errors.iter().map(|&(loan, _)| loan).collect()); + ctx.potential_subset_errors = Some(potential_subset_errors); + + datafrog_opt::compute(&ctx, &mut result) + } + } + Algorithm::Compare => { + // Ensure the `Naive` and `DatafrogOpt` errors are the same + let (naive_errors, naive_subset_errors) = naive::compute(&ctx, &mut result); + let (opt_errors, _) = datafrog_opt::compute(&ctx, &mut result); + + // TODO: compare illegal subset relations errors as well here ? + + let mut naive_errors_by_point = FxHashMap::default(); + for &(loan, point) in naive_errors.iter() { + naive_errors_by_point + .entry(point) + .or_insert_with(Vec::new) + .push(loan); + } + + let mut opt_errors_by_point = FxHashMap::default(); + for &(loan, point) in opt_errors.iter() { + opt_errors_by_point + .entry(point) + .or_insert_with(Vec::new) + .push(loan); + } + + if compare_errors(&naive_errors_by_point, &opt_errors_by_point) { + panic!(concat!( + "The errors reported by the naive algorithm differ from ", + "the errors reported by the optimized algorithm. ", + "See the error log for details." + )); + } else { + debug!("Naive and optimized algorithms reported the same errors."); + } + + (naive_errors, naive_subset_errors) + } + }; + + // Record illegal access errors + for &(loan, location) in errors.iter() { + result.errors.entry(location).or_default().push(loan); + } + + // Record illegal subset errors + for &(origin1, origin2, location) in subset_errors.iter() { + result + .subset_errors + .entry(location) + .or_default() + .insert((origin1, origin2)); + } + + // Record more debugging info when asked to do so + if dump_enabled { + for &(origin, location) in ctx.origin_live_on_entry.iter() { + result + .origin_live_on_entry + .entry(location) + .or_default() + .push(origin); + } + + for &(origin, loan) in ctx.known_contains.iter() { + result + .known_contains + .entry(origin) + .or_default() + .insert(loan); + } + } + + result + } + + /// Computes the transitive closure of the `known_placeholder_subset` relation, so that we have + /// the full list of placeholder loans contained by the placeholder origins. + fn compute_known_contains( + known_placeholder_subset: &Relation<(T::Origin, T::Origin)>, + placeholder: &[(T::Origin, T::Loan)], + ) -> Relation<(T::Origin, T::Loan)> { + let mut iteration = datafrog::Iteration::new(); + let known_contains = iteration.variable("known_contains"); + + // known_contains(Origin1, Loan1) :- + // placeholder(Origin1, Loan1). + known_contains.extend(placeholder.iter()); + + while iteration.changed() { + // known_contains(Origin2, Loan1) :- + // known_contains(Origin1, Loan1), + // known_placeholder_subset(Origin1, Origin2). + known_contains.from_join( + &known_contains, + known_placeholder_subset, + |&_origin1, &loan1, &origin2| (origin2, loan1), + ); + } + + known_contains.complete() + } + + /// Computes the transitive closure of the `known_placeholder_subset` relation. + fn compute_known_placeholder_subset( + known_placeholder_subset_base: &Relation<(T::Origin, T::Origin)>, + ) -> Relation<(T::Origin, T::Origin)> { + use datafrog::{Iteration, RelationLeaper}; + let mut iteration = Iteration::new(); + + let known_placeholder_subset = iteration.variable("known_placeholder_subset"); + + // known_placeholder_subset(Origin1, Origin2) :- + // known_placeholder_subset_base(Origin1, Origin2). + known_placeholder_subset.extend(known_placeholder_subset_base.iter()); + + while iteration.changed() { + // known_placeholder_subset(Origin1, Origin3) :- + // known_placeholder_subset(Origin1, Origin2), + // known_placeholder_subset_base(Origin2, Origin3). + known_placeholder_subset.from_leapjoin( + &known_placeholder_subset, + known_placeholder_subset_base.extend_with(|&(_origin1, origin2)| origin2), + |&(origin1, _origin2), &origin3| (origin1, origin3), + ); + } + + known_placeholder_subset.complete() + } + + fn new(dump_enabled: bool) -> Self { + Output { + errors: FxHashMap::default(), + subset_errors: FxHashMap::default(), + dump_enabled, + loan_live_at: FxHashMap::default(), + origin_contains_loan_at: FxHashMap::default(), + origin_contains_loan_anywhere: FxHashMap::default(), + origin_live_on_entry: FxHashMap::default(), + loan_invalidated_at: FxHashMap::default(), + move_errors: FxHashMap::default(), + subset: FxHashMap::default(), + subset_anywhere: FxHashMap::default(), + var_live_on_entry: FxHashMap::default(), + var_drop_live_on_entry: FxHashMap::default(), + path_maybe_initialized_on_exit: FxHashMap::default(), + path_maybe_uninitialized_on_exit: FxHashMap::default(), + var_maybe_partly_initialized_on_exit: FxHashMap::default(), + known_contains: FxHashMap::default(), + } + } + + pub fn errors_at(&self, location: T::Point) -> &[T::Loan] { + match self.errors.get(&location) { + Some(v) => v, + None => &[], + } + } + + pub fn loans_in_scope_at(&self, location: T::Point) -> &[T::Loan] { + match self.loan_live_at.get(&location) { + Some(p) => p, + None => &[], + } + } + + pub fn origin_contains_loan_at( + &self, + location: T::Point, + ) -> Cow<'_, BTreeMap<T::Origin, BTreeSet<T::Loan>>> { + assert!(self.dump_enabled); + match self.origin_contains_loan_at.get(&location) { + Some(map) => Cow::Borrowed(map), + None => Cow::Owned(BTreeMap::default()), + } + } + + pub fn origins_live_at(&self, location: T::Point) -> &[T::Origin] { + assert!(self.dump_enabled); + match self.origin_live_on_entry.get(&location) { + Some(v) => v, + None => &[], + } + } + + pub fn subsets_at( + &self, + location: T::Point, + ) -> Cow<'_, BTreeMap<T::Origin, BTreeSet<T::Origin>>> { + assert!(self.dump_enabled); + match self.subset.get(&location) { + Some(v) => Cow::Borrowed(v), + None => Cow::Owned(BTreeMap::default()), + } + } +} + +/// Compares errors reported by Naive implementation with the errors +/// reported by the optimized implementation. +fn compare_errors<Loan: Atom, Point: Atom>( + all_naive_errors: &FxHashMap<Point, Vec<Loan>>, + all_opt_errors: &FxHashMap<Point, Vec<Loan>>, +) -> bool { + let points = all_naive_errors.keys().chain(all_opt_errors.keys()); + + let mut differ = false; + for point in points { + let mut naive_errors = all_naive_errors.get(&point).cloned().unwrap_or_default(); + naive_errors.sort(); + + let mut opt_errors = all_opt_errors.get(&point).cloned().unwrap_or_default(); + opt_errors.sort(); + + for err in naive_errors.iter() { + if !opt_errors.contains(err) { + error!( + "Error {0:?} at {1:?} reported by naive, but not opt.", + err, point + ); + differ = true; + } + } + + for err in opt_errors.iter() { + if !naive_errors.contains(err) { + error!( + "Error {0:?} at {1:?} reported by opt, but not naive.", + err, point + ); + differ = true; + } + } + } + + differ +} + +#[cfg(test)] +mod tests { + use super::*; + + impl Atom for usize { + fn index(self) -> usize { + self + } + } + + fn compare( + errors1: &FxHashMap<usize, Vec<usize>>, + errors2: &FxHashMap<usize, Vec<usize>>, + ) -> bool { + let diff1 = compare_errors(errors1, errors2); + let diff2 = compare_errors(errors2, errors1); + assert_eq!(diff1, diff2); + diff1 + } + + #[test] + fn test_compare_errors() { + let empty = FxHashMap::default(); + assert_eq!(false, compare(&empty, &empty)); + let mut empty_vec = FxHashMap::default(); + empty_vec.insert(1, vec![]); + empty_vec.insert(2, vec![]); + assert_eq!(false, compare(&empty, &empty_vec)); + + let mut singleton1 = FxHashMap::default(); + singleton1.insert(1, vec![10]); + assert_eq!(false, compare(&singleton1, &singleton1)); + let mut singleton2 = FxHashMap::default(); + singleton2.insert(1, vec![11]); + assert_eq!(false, compare(&singleton2, &singleton2)); + let mut singleton3 = FxHashMap::default(); + singleton3.insert(2, vec![10]); + assert_eq!(false, compare(&singleton3, &singleton3)); + + assert_eq!(true, compare(&singleton1, &singleton2)); + assert_eq!(true, compare(&singleton2, &singleton3)); + assert_eq!(true, compare(&singleton1, &singleton3)); + + assert_eq!(true, compare(&empty, &singleton1)); + assert_eq!(true, compare(&empty, &singleton2)); + assert_eq!(true, compare(&empty, &singleton3)); + + let mut errors1 = FxHashMap::default(); + errors1.insert(1, vec![11]); + errors1.insert(2, vec![10]); + assert_eq!(false, compare(&errors1, &errors1)); + assert_eq!(true, compare(&errors1, &singleton1)); + assert_eq!(true, compare(&errors1, &singleton2)); + assert_eq!(true, compare(&errors1, &singleton3)); + } +} diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/polonius-engine/src/output/naive.rs b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/polonius-engine/src/output/naive.rs new file mode 100644 index 0000000..aa42048 --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/polonius-engine/src/output/naive.rs @@ -0,0 +1,299 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! A version of the Naive datalog analysis using Datafrog. + +use datafrog::{Iteration, Relation, RelationLeaper}; +use std::time::Instant; + +use crate::facts::FactTypes; +use crate::output::{Context, Output}; + +pub(super) fn compute<T: FactTypes>( + ctx: &Context<'_, T>, + result: &mut Output<T>, +) -> ( + Relation<(T::Loan, T::Point)>, + Relation<(T::Origin, T::Origin, T::Point)>, +) { + let timer = Instant::now(); + + let (errors, subset_errors) = { + // Static inputs + let origin_live_on_entry_rel = &ctx.origin_live_on_entry; + let cfg_edge = &ctx.cfg_edge; + let loan_killed_at = &ctx.loan_killed_at; + let known_placeholder_subset = &ctx.known_placeholder_subset; + let placeholder_origin = &ctx.placeholder_origin; + + // Create a new iteration context, ... + let mut iteration = Iteration::new(); + + // .. some variables, .. + let subset = iteration.variable::<(T::Origin, T::Origin, T::Point)>("subset"); + let origin_contains_loan_on_entry = + iteration.variable::<(T::Origin, T::Loan, T::Point)>("origin_contains_loan_on_entry"); + let loan_live_at = iteration.variable::<((T::Loan, T::Point), ())>("loan_live_at"); + + // `loan_invalidated_at` facts, stored ready for joins + let loan_invalidated_at = Relation::from_iter( + ctx.loan_invalidated_at + .iter() + .map(|&(loan, point)| ((loan, point), ())), + ); + + // different indices for `subset`. + let subset_o1p = iteration.variable_indistinct("subset_o1p"); + let subset_o2p = iteration.variable_indistinct("subset_o2p"); + + // different index for `origin_contains_loan_on_entry`. + let origin_contains_loan_on_entry_op = + iteration.variable_indistinct("origin_contains_loan_on_entry_op"); + + // Unfortunately, we need `origin_live_on_entry` in both variable and relation forms: + // We need: + // - `origin_live_on_entry` as a Relation for the leapjoins in rules 3 & 6 + // - `origin_live_on_entry` as a Variable for the join in rule 7 + // + // The leapjoins use `origin_live_on_entry` as `(Origin, Point)` tuples, while the join uses + // it as a `((O, P), ())` tuple to filter the `((Origin, Point), Loan)` tuples from + // `origin_contains_loan_on_entry_op`. + // + // The regular join in rule 7 could be turned into a `filter_with` leaper but that would + // result in a leapjoin with no `extend_*` leapers: a leapjoin that is not well-formed. + // Doing the filtering via an `extend_with` leaper would be extremely inefficient. + // + // Until there's an API in datafrog to handle this use-case better, we do a slightly less + // inefficient thing of copying the whole static input into a Variable to use a regular + // join, even though the liveness information can be quite heavy (around 1M tuples + // on `clap`). + // This is the Naive variant so this is not a big problem, but needs an + // explanation. + let origin_live_on_entry_var = + iteration.variable::<((T::Origin, T::Point), ())>("origin_live_on_entry"); + origin_live_on_entry_var.extend( + origin_live_on_entry_rel + .iter() + .map(|&(origin, point)| ((origin, point), ())), + ); + + // output relations: illegal accesses errors, and illegal subset relations errors + let errors = iteration.variable("errors"); + let subset_errors = iteration.variable::<(T::Origin, T::Origin, T::Point)>("subset_errors"); + + // load initial facts: + + // Rule 1: the initial subsets are the non-transitive `subset_base` static input. + // + // subset(Origin1, Origin2, Point) :- + // subset_base(Origin1, Origin2, Point). + subset.extend(ctx.subset_base.iter()); + + // Rule 4: the issuing origins are the ones initially containing loans. + // + // origin_contains_loan_on_entry(Origin, Loan, Point) :- + // loan_issued_at(Origin, Loan, Point). + origin_contains_loan_on_entry.extend(ctx.loan_issued_at.iter()); + + // .. and then start iterating rules! + while iteration.changed() { + // Cleanup step: remove symmetries + // - remove origins which are `subset`s of themselves + // + // FIXME: investigate whether is there a better way to do that without complicating + // the rules too much, because it would also require temporary variables and + // impact performance. Until then, the big reduction in tuples improves performance + // a lot, even if we're potentially adding a small number of tuples + // per round just to remove them in the next round. + subset + .recent + .borrow_mut() + .elements + .retain(|&(origin1, origin2, _)| origin1 != origin2); + + // Remap fields to re-index by keys, to prepare the data needed by the rules below. + subset_o1p.from_map(&subset, |&(origin1, origin2, point)| { + ((origin1, point), origin2) + }); + subset_o2p.from_map(&subset, |&(origin1, origin2, point)| { + ((origin2, point), origin1) + }); + + origin_contains_loan_on_entry_op + .from_map(&origin_contains_loan_on_entry, |&(origin, loan, point)| { + ((origin, point), loan) + }); + + // Rule 1: done above, as part of the static input facts setup. + + // Rule 2: compute the subset transitive closure, at a given point. + // + // subset(Origin1, Origin3, Point) :- + // subset(Origin1, Origin2, Point), + // subset(Origin2, Origin3, Point). + subset.from_join( + &subset_o2p, + &subset_o1p, + |&(_origin2, point), &origin1, &origin3| (origin1, origin3, point), + ); + + // Rule 3: propagate subsets along the CFG, according to liveness. + // + // subset(Origin1, Origin2, Point2) :- + // subset(Origin1, Origin2, Point1), + // cfg_edge(Point1, Point2), + // origin_live_on_entry(Origin1, Point2), + // origin_live_on_entry(Origin2, Point2). + subset.from_leapjoin( + &subset, + ( + cfg_edge.extend_with(|&(_origin1, _origin2, point1)| point1), + origin_live_on_entry_rel.extend_with(|&(origin1, _origin2, _point1)| origin1), + origin_live_on_entry_rel.extend_with(|&(_origin1, origin2, _point1)| origin2), + ), + |&(origin1, origin2, _point1), &point2| (origin1, origin2, point2), + ); + + // Rule 4: done above as part of the static input facts setup. + + // Rule 5: propagate loans within origins, at a given point, according to subsets. + // + // origin_contains_loan_on_entry(Origin2, Loan, Point) :- + // origin_contains_loan_on_entry(Origin1, Loan, Point), + // subset(Origin1, Origin2, Point). + origin_contains_loan_on_entry.from_join( + &origin_contains_loan_on_entry_op, + &subset_o1p, + |&(_origin1, point), &loan, &origin2| (origin2, loan, point), + ); + + // Rule 6: propagate loans along the CFG, according to liveness. + // + // origin_contains_loan_on_entry(Origin, Loan, Point2) :- + // origin_contains_loan_on_entry(Origin, Loan, Point1), + // !loan_killed_at(Loan, Point1), + // cfg_edge(Point1, Point2), + // origin_live_on_entry(Origin, Point2). + origin_contains_loan_on_entry.from_leapjoin( + &origin_contains_loan_on_entry, + ( + loan_killed_at.filter_anti(|&(_origin, loan, point1)| (loan, point1)), + cfg_edge.extend_with(|&(_origin, _loan, point1)| point1), + origin_live_on_entry_rel.extend_with(|&(origin, _loan, _point1)| origin), + ), + |&(origin, loan, _point1), &point2| (origin, loan, point2), + ); + + // Rule 7: compute whether a loan is live at a given point, i.e. whether it is + // contained in a live origin at this point. + // + // loan_live_at(Loan, Point) :- + // origin_contains_loan_on_entry(Origin, Loan, Point), + // origin_live_on_entry(Origin, Point). + loan_live_at.from_join( + &origin_contains_loan_on_entry_op, + &origin_live_on_entry_var, + |&(_origin, point), &loan, _| ((loan, point), ()), + ); + + // Rule 8: compute illegal access errors, i.e. an invalidation of a live loan. + // + // Here again, this join acts as a pure filter and could be a more efficient leapjoin. + // However, similarly to the `origin_live_on_entry` example described above, the + // leapjoin with a single `filter_with` leaper would currently not be well-formed. + // We don't explictly need to materialize `loan_live_at` either, and that doesn't + // change the well-formedness situation, so we still materialize it (since that also + // helps in testing). + // + // errors(Loan, Point) :- + // loan_invalidated_at(Loan, Point), + // loan_live_at(Loan, Point). + errors.from_join( + &loan_live_at, + &loan_invalidated_at, + |&(loan, point), _, _| (loan, point), + ); + + // Rule 9: compute illegal subset relations errors, i.e. the undeclared subsets + // between two placeholder origins. + // Here as well, WF-ness prevents this join from being a filter-only leapjoin. It + // doesn't matter much, as `placeholder_origin` is single-value relation. + // + // subset_error(Origin1, Origin2, Point) :- + // subset(Origin1, Origin2, Point), + // placeholder_origin(Origin1), + // placeholder_origin(Origin2), + // !known_placeholder_subset(Origin1, Origin2). + subset_errors.from_leapjoin( + &subset, + ( + placeholder_origin.extend_with(|&(origin1, _origin2, _point)| origin1), + placeholder_origin.extend_with(|&(_origin1, origin2, _point)| origin2), + known_placeholder_subset + .filter_anti(|&(origin1, origin2, _point)| (origin1, origin2)), + // remove symmetries: + datafrog::ValueFilter::from(|&(origin1, origin2, _point), _| { + origin1 != origin2 + }), + ), + |&(origin1, origin2, point), _| (origin1, origin2, point), + ); + } + + // Handle verbose output data + if result.dump_enabled { + let subset = subset.complete(); + assert!( + subset + .iter() + .filter(|&(origin1, origin2, _)| origin1 == origin2) + .count() + == 0, + "unwanted subset symmetries" + ); + for &(origin1, origin2, location) in subset.iter() { + result + .subset + .entry(location) + .or_default() + .entry(origin1) + .or_default() + .insert(origin2); + } + + let origin_contains_loan_on_entry = origin_contains_loan_on_entry.complete(); + for &(origin, loan, location) in origin_contains_loan_on_entry.iter() { + result + .origin_contains_loan_at + .entry(location) + .or_default() + .entry(origin) + .or_default() + .insert(loan); + } + + let loan_live_at = loan_live_at.complete(); + for &((loan, location), _) in loan_live_at.iter() { + result.loan_live_at.entry(location).or_default().push(loan); + } + } + + (errors.complete(), subset_errors.complete()) + }; + + info!( + "analysis done: {} `errors` tuples, {} `subset_errors` tuples, {:?}", + errors.len(), + subset_errors.len(), + timer.elapsed() + ); + + (errors, subset_errors) +} diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/rustc-hash/.cargo-checksum.json b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/rustc-hash/.cargo-checksum.json new file mode 100644 index 0000000..544af9f --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/rustc-hash/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"CODE_OF_CONDUCT.md":"edca092fde496419a9f1ba640048aa0270b62dfea576cd3175f0b53e3c230470","Cargo.toml":"647814b27b6fc4fbef1df70d796b53b723e776b68467372044e4182763007379","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"cac8197ac869d64a6efc26cab883a269392ae6db51f7453bca722f8f31d67c7c","src/lib.rs":"ddecafb5db609d0d8eebd19e4d98dc865e7e9282a4183421f9bd765c01a231c0"},"package":"08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"}
\ No newline at end of file diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/rustc-hash/CODE_OF_CONDUCT.md b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/rustc-hash/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..d70b2b5 --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/rustc-hash/CODE_OF_CONDUCT.md @@ -0,0 +1,40 @@ +# The Rust Code of Conduct + +A version of this document [can be found online](https://www.rust-lang.org/conduct.html). + +## Conduct + +**Contact**: [rust-mods@rust-lang.org](mailto:rust-mods@rust-lang.org) + +* We are committed to providing a friendly, safe and welcoming environment for all, regardless of level of experience, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, nationality, or other similar characteristic. +* On IRC, please avoid using overtly sexual nicknames or other nicknames that might detract from a friendly, safe and welcoming environment for all. +* Please be kind and courteous. There's no need to be mean or rude. +* Respect that people have differences of opinion and that every design or implementation choice carries a trade-off and numerous costs. There is seldom a right answer. +* Please keep unstructured critique to a minimum. If you have solid ideas you want to experiment with, make a fork and see how it works. +* We will exclude you from interaction if you insult, demean or harass anyone. That is not welcome behavior. We interpret the term "harassment" as including the definition in the <a href="http://citizencodeofconduct.org/">Citizen Code of Conduct</a>; if you have any lack of clarity about what might be included in that concept, please read their definition. In particular, we don't tolerate behavior that excludes people in socially marginalized groups. +* Private harassment is also unacceptable. No matter who you are, if you feel you have been or are being harassed or made uncomfortable by a community member, please contact one of the channel ops or any of the [Rust moderation team][mod_team] immediately. Whether you're a regular contributor or a newcomer, we care about making this community a safe place for you and we've got your back. +* Likewise any spamming, trolling, flaming, baiting or other attention-stealing behavior is not welcome. + +## Moderation + + +These are the policies for upholding our community's standards of conduct. If you feel that a thread needs moderation, please contact the [Rust moderation team][mod_team]. + +1. Remarks that violate the Rust standards of conduct, including hateful, hurtful, oppressive, or exclusionary remarks, are not allowed. (Cursing is allowed, but never targeting another user, and never in a hateful manner.) +2. Remarks that moderators find inappropriate, whether listed in the code of conduct or not, are also not allowed. +3. Moderators will first respond to such remarks with a warning. +4. If the warning is unheeded, the user will be "kicked," i.e., kicked out of the communication channel to cool off. +5. If the user comes back and continues to make trouble, they will be banned, i.e., indefinitely excluded. +6. Moderators may choose at their discretion to un-ban the user if it was a first offense and they offer the offended party a genuine apology. +7. If a moderator bans someone and you think it was unjustified, please take it up with that moderator, or with a different moderator, **in private**. Complaints about bans in-channel are not allowed. +8. Moderators are held to a higher standard than other community members. If a moderator creates an inappropriate situation, they should expect less leeway than others. + +In the Rust community we strive to go the extra step to look out for each other. Don't just aim to be technically unimpeachable, try to be your best self. In particular, avoid flirting with offensive or sensitive issues, particularly if they're off-topic; this all too often leads to unnecessary fights, hurt feelings, and damaged trust; worse, it can drive people away from the community entirely. + +And if someone takes issue with something you said or did, resist the urge to be defensive. Just stop doing what it was they complained about and apologize. Even if you feel you were misinterpreted or unfairly accused, chances are good there was something you could've communicated better — remember that it's your responsibility to make your fellow Rustaceans comfortable. Everyone wants to get along and we are all here first and foremost because we want to talk about cool technology. You will find that people will be eager to assume good intent and forgive as long as you earn their trust. + +The enforcement policies listed above apply to all official Rust venues; including official IRC channels (#rust, #rust-internals, #rust-tools, #rust-libs, #rustc, #rust-beginners, #rust-docs, #rust-community, #rust-lang, and #cargo); GitHub repositories under rust-lang, rust-lang-nursery, and rust-lang-deprecated; and all forums under rust-lang.org (users.rust-lang.org, internals.rust-lang.org). For other projects adopting the Rust Code of Conduct, please contact the maintainers of those projects for enforcement. If you wish to use this code of conduct for your own project, consider explicitly mentioning your moderation policy or making a copy with your own moderation policy so as to avoid confusion. + +*Adapted from the [Node.js Policy on Trolling](http://blog.izs.me/post/30036893703/policy-on-trolling) as well as the [Contributor Covenant v1.3.0](https://www.contributor-covenant.org/version/1/3/0/).* + +[mod_team]: https://www.rust-lang.org/team.html#Moderation-team diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/rustc-hash/Cargo.toml b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/rustc-hash/Cargo.toml new file mode 100644 index 0000000..47330b7 --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/rustc-hash/Cargo.toml @@ -0,0 +1,25 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies +# +# If you believe there's an error in this file please file an +# issue against the rust-lang/cargo repository. If you're +# editing this file be aware that the upstream Cargo.toml +# will likely look very different (and much more reasonable) + +[package] +name = "rustc-hash" +version = "1.1.0" +authors = ["The Rust Project Developers"] +description = "speed, non-cryptographic hash used in rustc" +readme = "README.md" +keywords = ["hash", "fxhash", "rustc"] +license = "Apache-2.0/MIT" +repository = "https://github.com/rust-lang-nursery/rustc-hash" + +[features] +default = ["std"] +std = [] diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/rustc-hash/LICENSE-APACHE b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/rustc-hash/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/rustc-hash/LICENSE-APACHE @@ -0,0 +1,201 @@ + 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. diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/rustc-hash/LICENSE-MIT b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/rustc-hash/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/rustc-hash/LICENSE-MIT @@ -0,0 +1,23 @@ +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. diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/rustc-hash/README.md b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/rustc-hash/README.md new file mode 100644 index 0000000..e33057a --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/rustc-hash/README.md @@ -0,0 +1,38 @@ +# rustc-hash + +[](https://crates.io/crates/rustc-hash) +[](https://docs.rs/rustc-hash) + +A speedy hash algorithm used within rustc. The hashmap in liballoc by +default uses SipHash which isn't quite as speedy as we want. In the +compiler we're not really worried about DOS attempts, so we use a fast +non-cryptographic hash. + +This is the same as the algorithm used by Firefox -- which is a +homespun one not based on any widely-known algorithm -- though +modified to produce 64-bit hash values instead of 32-bit hash +values. It consistently out-performs an FNV-based hash within rustc +itself -- the collision rate is similar or slightly worse than FNV, +but the speed of the hash function itself is much higher because it +works on up to 8 bytes at a time. + +## Usage + +```rust +use rustc_hash::FxHashMap; + +let mut map: FxHashMap<u32, u32> = FxHashMap::default(); +map.insert(22, 44); +``` + +### `no_std` + +This crate can be used as a `no_std` crate by disabling the `std` +feature, which is on by default, as follows: + +```toml +rustc-hash = { version = "1.0", default-features = false } +``` + +In this configuration, `FxHasher` is the only export, and the +`FxHashMap`/`FxHashSet` type aliases are omitted. diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/rustc-hash/src/lib.rs b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/rustc-hash/src/lib.rs new file mode 100644 index 0000000..ee9ad31 --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/vendor/rustc-hash/src/lib.rs @@ -0,0 +1,148 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Fast, non-cryptographic hash used by rustc and Firefox. +//! +//! # Example +//! +//! ```rust +//! # #[cfg(feature = "std")] +//! # fn main() { +//! use rustc_hash::FxHashMap; +//! let mut map: FxHashMap<u32, u32> = FxHashMap::default(); +//! map.insert(22, 44); +//! # } +//! # #[cfg(not(feature = "std"))] +//! # fn main() { } +//! ``` + +#![no_std] + +#[cfg(feature = "std")] +extern crate std; + +use core::convert::TryInto; +use core::default::Default; +#[cfg(feature = "std")] +use core::hash::BuildHasherDefault; +use core::hash::Hasher; +use core::mem::size_of; +use core::ops::BitXor; +#[cfg(feature = "std")] +use std::collections::{HashMap, HashSet}; + +/// Type alias for a hashmap using the `fx` hash algorithm. +#[cfg(feature = "std")] +pub type FxHashMap<K, V> = HashMap<K, V, BuildHasherDefault<FxHasher>>; + +/// Type alias for a hashmap using the `fx` hash algorithm. +#[cfg(feature = "std")] +pub type FxHashSet<V> = HashSet<V, BuildHasherDefault<FxHasher>>; + +/// A speedy hash algorithm for use within rustc. The hashmap in liballoc +/// by default uses SipHash which isn't quite as speedy as we want. In the +/// compiler we're not really worried about DOS attempts, so we use a fast +/// non-cryptographic hash. +/// +/// This is the same as the algorithm used by Firefox -- which is a homespun +/// one not based on any widely-known algorithm -- though modified to produce +/// 64-bit hash values instead of 32-bit hash values. It consistently +/// out-performs an FNV-based hash within rustc itself -- the collision rate is +/// similar or slightly worse than FNV, but the speed of the hash function +/// itself is much higher because it works on up to 8 bytes at a time. +pub struct FxHasher { + hash: usize, +} + +#[cfg(target_pointer_width = "32")] +const K: usize = 0x9e3779b9; +#[cfg(target_pointer_width = "64")] +const K: usize = 0x517cc1b727220a95; + +impl Default for FxHasher { + #[inline] + fn default() -> FxHasher { + FxHasher { hash: 0 } + } +} + +impl FxHasher { + #[inline] + fn add_to_hash(&mut self, i: usize) { + self.hash = self.hash.rotate_left(5).bitxor(i).wrapping_mul(K); + } +} + +impl Hasher for FxHasher { + #[inline] + fn write(&mut self, mut bytes: &[u8]) { + #[cfg(target_pointer_width = "32")] + let read_usize = |bytes: &[u8]| u32::from_ne_bytes(bytes[..4].try_into().unwrap()); + #[cfg(target_pointer_width = "64")] + let read_usize = |bytes: &[u8]| u64::from_ne_bytes(bytes[..8].try_into().unwrap()); + + let mut hash = FxHasher { hash: self.hash }; + assert!(size_of::<usize>() <= 8); + while bytes.len() >= size_of::<usize>() { + hash.add_to_hash(read_usize(bytes) as usize); + bytes = &bytes[size_of::<usize>()..]; + } + if (size_of::<usize>() > 4) && (bytes.len() >= 4) { + hash.add_to_hash(u32::from_ne_bytes(bytes[..4].try_into().unwrap()) as usize); + bytes = &bytes[4..]; + } + if (size_of::<usize>() > 2) && bytes.len() >= 2 { + hash.add_to_hash(u16::from_ne_bytes(bytes[..2].try_into().unwrap()) as usize); + bytes = &bytes[2..]; + } + if (size_of::<usize>() > 1) && bytes.len() >= 1 { + hash.add_to_hash(bytes[0] as usize); + } + self.hash = hash.hash; + } + + #[inline] + fn write_u8(&mut self, i: u8) { + self.add_to_hash(i as usize); + } + + #[inline] + fn write_u16(&mut self, i: u16) { + self.add_to_hash(i as usize); + } + + #[inline] + fn write_u32(&mut self, i: u32) { + self.add_to_hash(i as usize); + } + + #[cfg(target_pointer_width = "32")] + #[inline] + fn write_u64(&mut self, i: u64) { + self.add_to_hash(i as usize); + self.add_to_hash((i >> 32) as usize); + } + + #[cfg(target_pointer_width = "64")] + #[inline] + fn write_u64(&mut self, i: u64) { + self.add_to_hash(i as usize); + } + + #[inline] + fn write_usize(&mut self, i: usize) { + self.add_to_hash(i); + } + + #[inline] + fn finish(&self) -> u64 { + self.hash as u64 + } +} diff --git a/gcc/rust/checks/errors/borrowck/polonius/rust-polonius.h b/gcc/rust/checks/errors/borrowck/polonius/rust-polonius.h index b013a93..0ce2142 100644 --- a/gcc/rust/checks/errors/borrowck/polonius/rust-polonius.h +++ b/gcc/rust/checks/errors/borrowck/polonius/rust-polonius.h @@ -35,7 +35,7 @@ struct FullPoint bool mid; /** Expands a compressed `Point` into its components. - * See `Point` docs for encoding details. + * See `Point` docs for encoding details in ./rust-polonius-ffi.h */ explicit FullPoint (Point point) : bb (extract_bb (point)), stmt (extract_stmt (point)), @@ -45,7 +45,24 @@ struct FullPoint static uint32_t extract_bb (Point point) { return point >> 16; } static uint32_t extract_stmt (Point point) { - return (point & ~(1 << 16)) >> 1; + // Point is a 32 bit unsigned integer + // 16 15 1 + // xxxxxxxxxxxxxxxx xxxxxxxxxxxxxxx x + // ^~~~~~~~~~~~~~~~ ^~~~~~~~~~~~~~~ ^ + // | | | + // basic_block | start/mid + // statement + // the left most 16 bits store the basic block number + // the right most bit, represents the start/mid status + // the remaining 15 bits between these two represent the statement number + // which we need to extract in this fucntion + // + // firstly we can get rid of right most bit by performing left shift once + auto hide_left_most_bit = point >> 1; + // now we only need the 15 bits on the right + // we can mask the remaining bits by performing bitwise AND with fifteen + // 1's which in hexadecimal is 0x7FFF + return hide_left_most_bit & 0x7FFF; } static bool extract_mid (Point point) { return point & 1; } diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.cc b/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.cc index acfcdd8..d6acc6a 100644 --- a/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.cc +++ b/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.cc @@ -21,6 +21,7 @@ #include "rust-bir-builder-lazyboolexpr.h" #include "rust-bir-builder-pattern.h" #include "rust-bir-builder-struct.h" +#include "rust-hir-expr.h" namespace Rust { namespace BIR { @@ -44,7 +45,8 @@ ExprStmtBuilder::setup_loop (HIR::BaseLoopExpr &expr) BasicBlockId break_bb = new_bb (); // We are still outside the loop block; - ScopeId continue_scope = ctx.place_db.get_current_scope_id () + 1; + ScopeId continue_scope + = ctx.place_db.get_current_scope_id ().next_scope_id (); ctx.loop_and_label_stack.emplace_back (true, label, label_var, break_bb, continue_bb, continue_scope); @@ -80,14 +82,21 @@ ExprStmtBuilder::visit (HIR::ClosureExpr &expr) { auto closure_ty = lookup_type (expr)->as<TyTy::ClosureType> (); std::vector<PlaceId> captures; + std::vector<location_t> capture_locations; for (auto &capture : closure_ty->get_captures ()) { captures.push_back (ctx.place_db.lookup_variable (capture)); + auto location = Analysis::Mappings::get () + .lookup_ast_item (capture) + .value () + ->get_locus (); + capture_locations.push_back (location); } - move_all (captures); + move_all (captures, capture_locations); // Note: Not a coercion site for captures. - return_expr (new InitializerExpr (std::move (captures)), lookup_type (expr)); + return_expr (new InitializerExpr (std::move (captures)), lookup_type (expr), + expr.get_locus ()); } void @@ -96,46 +105,55 @@ ExprStmtBuilder::visit (HIR::StructExprStructFields &fields) auto *p_adt_type = lookup_type (fields)->as<TyTy::ADTType> (); auto struct_ty = p_adt_type->get_variants ().at (0); auto init_values = StructBuilder (ctx, struct_ty).build (fields); - move_all (init_values); + // collect fields locations + std::vector<location_t> field_locations; + for (auto &field : fields.get_fields ()) + { + field_locations.push_back (field->get_locus ()); + } + move_all (init_values, field_locations); return_expr (new InitializerExpr (std::move (init_values)), - lookup_type (fields)); + lookup_type (fields), fields.get_locus ()); } void ExprStmtBuilder::visit (HIR::StructExprStruct &expr) { // There is no way to modify empty struct, which makes them constant. - return_place (ctx.place_db.get_constant (lookup_type (expr))); + return_place (ctx.place_db.get_constant (lookup_type (expr)), + expr.get_locus ()); } void ExprStmtBuilder::visit (HIR::LiteralExpr &expr) { // Different literal values of the same type are not distinguished in BIR. - return_place (ctx.place_db.get_constant (lookup_type (expr))); + return_place (ctx.place_db.get_constant (lookup_type (expr)), + expr.get_locus ()); } void ExprStmtBuilder::visit (HIR::BorrowExpr &expr) { - auto operand = visit_expr (*expr.get_expr ()); + auto operand = visit_expr (expr.get_expr ()); if (ctx.place_db[operand].is_constant ()) { // Cannot borrow a constant, must create a temporary copy. - push_tmp_assignment (operand); + push_tmp_assignment (operand, expr.get_locus ()); operand = translated; } // BorrowExpr cannot be annotated with lifetime. - return_borrowed (operand, lookup_type (expr)); + return_borrowed (operand, lookup_type (expr), expr.get_locus ()); } void ExprStmtBuilder::visit (HIR::DereferenceExpr &expr) { - auto operand = visit_expr (*expr.get_expr ()); + auto operand = visit_expr (expr.get_expr ()); return_place (ctx.place_db.lookup_or_add_path (Place::DEREF, - lookup_type (expr), operand)); + lookup_type (expr), operand), + expr.get_locus ()); } void @@ -148,26 +166,32 @@ ExprStmtBuilder::visit (HIR::ErrorPropagationExpr &expr) void ExprStmtBuilder::visit (HIR::NegationExpr &expr) { - PlaceId operand = visit_expr (*expr.get_expr ()); - return_expr (new Operator<1> ({move_place (operand)}), lookup_type (expr)); + PlaceId operand = visit_expr (expr.get_expr ()); + return_expr (new Operator<1> ( + {move_place (operand, expr.get_expr ().get_locus ())}), + lookup_type (expr), expr.get_locus ()); } void ExprStmtBuilder::visit (HIR::ArithmeticOrLogicalExpr &expr) { - PlaceId lhs = visit_expr (*expr.get_lhs ()); - PlaceId rhs = visit_expr (*expr.get_rhs ()); - return_expr (new Operator<2> ({move_place (lhs), move_place (rhs)}), - lookup_type (expr)); + PlaceId lhs = visit_expr (expr.get_lhs ()); + PlaceId rhs = visit_expr (expr.get_rhs ()); + return_expr (new Operator<2> ( + {move_place (lhs, expr.get_lhs ().get_locus ()), + move_place (rhs, expr.get_rhs ().get_locus ())}), + lookup_type (expr), expr.get_locus ()); } void ExprStmtBuilder::visit (HIR::ComparisonExpr &expr) { - PlaceId lhs = visit_expr (*expr.get_lhs ()); - PlaceId rhs = visit_expr (*expr.get_rhs ()); - return_expr (new Operator<2> ({move_place (lhs), move_place (rhs)}), - lookup_type (expr)); + PlaceId lhs = visit_expr (expr.get_lhs ()); + PlaceId rhs = visit_expr (expr.get_rhs ()); + return_expr (new Operator<2> ( + {move_place (lhs, expr.get_lhs ().get_locus ()), + move_place (rhs, expr.get_rhs ().get_locus ())}), + lookup_type (expr), expr.get_locus ()); } void @@ -175,57 +199,66 @@ ExprStmtBuilder::visit (HIR::LazyBooleanExpr &expr) { return_place (LazyBooleanExprBuilder (ctx, take_or_create_return_place ( lookup_type (expr))) - .build (expr)); + .build (expr), + expr.get_locus ()); } void ExprStmtBuilder::visit (HIR::TypeCastExpr &expr) { - auto operand = visit_expr (*expr.get_expr ()); - return_expr (new Operator<1> ({operand}), lookup_type (expr)); + auto operand = visit_expr (expr.get_expr ()); + return_expr (new Operator<1> ({operand}), lookup_type (expr), + expr.get_locus ()); } void ExprStmtBuilder::visit (HIR::AssignmentExpr &expr) { - auto lhs = visit_expr (*expr.get_lhs ()); - auto rhs = visit_expr (*expr.get_rhs ()); - push_assignment (lhs, rhs); + auto lhs = visit_expr (expr.get_lhs ()); + auto rhs = visit_expr (expr.get_rhs ()); + push_assignment (lhs, rhs, expr.get_locus ()); translated = INVALID_PLACE; } void ExprStmtBuilder::visit (HIR::CompoundAssignmentExpr &expr) { - auto lhs = visit_expr (*expr.get_lhs ()); - auto rhs = visit_expr (*expr.get_rhs ()); - push_assignment (lhs, new Operator<2> ({lhs, rhs})); + auto lhs = visit_expr (expr.get_lhs ()); + auto rhs = visit_expr (expr.get_rhs ()); + push_assignment (lhs, new Operator<2> ({lhs, rhs}), expr.get_locus ()); } void ExprStmtBuilder::visit (HIR::GroupedExpr &expr) { - return_place (visit_expr (*expr.get_expr_in_parens ())); + return_place (visit_expr (expr.get_expr_in_parens ()), expr.get_locus ()); } void ExprStmtBuilder::visit (HIR::ArrayExpr &expr) { auto &elems = expr.get_internal_elements (); - switch (elems->get_array_expr_type ()) + switch (elems.get_array_expr_type ()) { case HIR::ArrayElems::VALUES: { - auto &elem_vals = (static_cast<HIR::ArrayElemsValues &> (*elems)); + auto &elem_vals = (static_cast<HIR::ArrayElemsValues &> (elems)); auto init_values = visit_list (elem_vals.get_values ()); - move_all (init_values); + // collect locations + std::vector<location_t> value_locations; + for (auto &value : elem_vals.get_values ()) + { + value_locations.push_back (value->get_locus ()); + } + move_all (init_values, value_locations); return_expr (new InitializerExpr (std::move (init_values)), - lookup_type (expr)); + lookup_type (expr), expr.get_locus ()); break; } case HIR::ArrayElems::COPIED: { - auto &elem_copied = (static_cast<HIR::ArrayElemsCopied &> (*elems)); - auto init = visit_expr (*elem_copied.get_elem_to_copy ()); - return_expr (new InitializerExpr ({init}), lookup_type (expr)); + auto &elem_copied = (static_cast<HIR::ArrayElemsCopied &> (elems)); + auto init = visit_expr (elem_copied.get_elem_to_copy ()); + return_expr (new InitializerExpr ({init}), lookup_type (expr), + expr.get_locus ()); break; } } @@ -234,12 +267,13 @@ ExprStmtBuilder::visit (HIR::ArrayExpr &expr) void ExprStmtBuilder::visit (HIR::ArrayIndexExpr &expr) { - auto lhs = visit_expr (*expr.get_array_expr ()); - auto rhs = visit_expr (*expr.get_index_expr ()); + auto lhs = visit_expr (expr.get_array_expr ()); + auto rhs = visit_expr (expr.get_index_expr ()); // The index is not tracked in BIR. std::ignore = rhs; - return_place ( - ctx.place_db.lookup_or_add_path (Place::INDEX, lookup_type (expr), lhs)); + return_place (ctx.place_db.lookup_or_add_path (Place::INDEX, + lookup_type (expr), lhs), + expr.get_locus ()); } void @@ -247,22 +281,23 @@ ExprStmtBuilder::visit (HIR::TupleExpr &expr) { std::vector<PlaceId> init_values = visit_list (expr.get_tuple_elems ()); return_expr (new InitializerExpr (std::move (init_values)), - lookup_type (expr)); + lookup_type (expr), expr.get_locus ()); } void ExprStmtBuilder::visit (HIR::TupleIndexExpr &expr) { - auto tuple = visit_expr (*expr.get_tuple_expr ()); + auto tuple = visit_expr (expr.get_tuple_expr ()); return_place (ctx.place_db.lookup_or_add_path (Place::FIELD, lookup_type (expr), tuple, - expr.get_tuple_index ())); + expr.get_tuple_index ()), + expr.get_locus ()); } void ExprStmtBuilder::visit (HIR::CallExpr &expr) { - PlaceId fn = visit_expr (*expr.get_fnexpr ()); + PlaceId fn = visit_expr (expr.get_fnexpr ()); std::vector<PlaceId> arguments = visit_list (expr.get_arguments ()); const auto fn_type @@ -273,20 +308,30 @@ ExprStmtBuilder::visit (HIR::CallExpr &expr) coercion_site (arguments[i], fn_type->get_param_type_at (i)); } - move_all (arguments); + // collect parameter locations + std::vector<location_t> parameter_locations; + for (auto ¶meter : expr.get_arguments ()) + { + parameter_locations.push_back (parameter->get_locus ()); + } + move_all (arguments, parameter_locations); return_expr (new CallExpr (fn, std::move (arguments)), lookup_type (expr), - true); + expr.get_locus (), true); } void +ExprStmtBuilder::visit (HIR::InlineAsm &expr) +{} + +void ExprStmtBuilder::visit (HIR::MethodCallExpr &expr) {} void ExprStmtBuilder::visit (HIR::FieldAccessExpr &expr) { - auto receiver = visit_expr (*expr.get_receiver_expr ()); + auto receiver = visit_expr (expr.get_receiver_expr ()); auto type = autoderef (receiver); rust_assert (type->get_kind () == TyTy::ADT); auto adt = type->as<TyTy::ADTType> (); @@ -302,7 +347,8 @@ ExprStmtBuilder::visit (HIR::FieldAccessExpr &expr) return_place (ctx.place_db.lookup_or_add_path (Place::FIELD, field_ty->get_field_type (), - receiver, field_index)); + receiver, field_index), + expr.get_locus ()); } void @@ -338,7 +384,8 @@ ExprStmtBuilder::visit (HIR::BlockExpr &block) if (block.has_expr () && !unreachable) { push_assignment (block_ctx.label_var, - visit_expr (*block.get_final_expr ())); + visit_expr (block.get_final_expr ()), + block.get_start_locus ()); } if (!ctx.get_current_bb ().is_terminated ()) { @@ -347,13 +394,14 @@ ExprStmtBuilder::visit (HIR::BlockExpr &block) ctx.current_bb = block_ctx.break_bb; ctx.loop_and_label_stack.pop_back (); - return_place (block_ctx.label_var); + return_place (block_ctx.label_var, block.get_start_locus ()); } else if (block.has_expr () && !unreachable) { - return_place (visit_expr (*block.get_final_expr (), + return_place (visit_expr (block.get_final_expr (), take_or_create_return_place ( - lookup_type (*block.get_final_expr ())))); + lookup_type (block.get_final_expr ()))), + block.get_start_locus ()); } if (!unreachable) @@ -368,7 +416,7 @@ ExprStmtBuilder::visit (HIR::ContinueExpr &cont) LoopAndLabelCtx info = cont.has_label () ? get_label_ctx (cont.get_label ()) : get_unnamed_loop_ctx (); start_new_consecutive_bb (); - unwind_until (info.continue_bb); + unwind_until (info.continue_scope); push_goto (info.continue_bb); // No code allowed after continue. Handled in BlockExpr. } @@ -379,7 +427,8 @@ ExprStmtBuilder::visit (HIR::BreakExpr &brk) LoopAndLabelCtx info = brk.has_label () ? get_label_ctx (brk.get_label ()) : get_unnamed_loop_ctx (); if (brk.has_break_expr ()) - push_assignment (info.label_var, visit_expr (*brk.get_expr ())); + push_assignment (info.label_var, visit_expr (brk.get_expr ()), + brk.get_locus ()); start_new_consecutive_bb (); unwind_until (ctx.place_db.get_scope (info.continue_scope).parent); @@ -390,44 +439,49 @@ ExprStmtBuilder::visit (HIR::BreakExpr &brk) void ExprStmtBuilder::visit (HIR::RangeFromToExpr &range) { - auto from = visit_expr (*range.get_from_expr ()); - auto to = visit_expr (*range.get_to_expr ()); - return_expr (new InitializerExpr ({from, to}), lookup_type (range)); + auto from = visit_expr (range.get_from_expr ()); + auto to = visit_expr (range.get_to_expr ()); + return_expr (new InitializerExpr ({from, to}), lookup_type (range), + range.get_locus ()); } void ExprStmtBuilder::visit (HIR::RangeFromExpr &expr) { - auto from = visit_expr (*expr.get_from_expr ()); - return_expr (new InitializerExpr ({from}), lookup_type (expr)); + auto from = visit_expr (expr.get_from_expr ()); + return_expr (new InitializerExpr ({from}), lookup_type (expr), + expr.get_locus ()); } void ExprStmtBuilder::visit (HIR::RangeToExpr &expr) { - auto to = visit_expr (*expr.get_to_expr ()); - return_expr (new InitializerExpr ({to}), lookup_type (expr)); + auto to = visit_expr (expr.get_to_expr ()); + return_expr (new InitializerExpr ({to}), lookup_type (expr), + expr.get_locus ()); } void ExprStmtBuilder::visit (HIR::RangeFullExpr &expr) { - return_expr (new InitializerExpr ({}), lookup_type (expr)); + return_expr (new InitializerExpr ({}), lookup_type (expr), expr.get_locus ()); } void ExprStmtBuilder::visit (HIR::RangeFromToInclExpr &expr) { - auto from = visit_expr (*expr.get_from_expr ()); - auto to = visit_expr (*expr.get_to_expr ()); - return_expr (new InitializerExpr ({from, to}), lookup_type (expr)); + auto from = visit_expr (expr.get_from_expr ()); + auto to = visit_expr (expr.get_to_expr ()); + return_expr (new InitializerExpr ({from, to}), lookup_type (expr), + expr.get_locus ()); } void ExprStmtBuilder::visit (HIR::RangeToInclExpr &expr) { - auto to = visit_expr (*expr.get_to_expr ()); - return_expr (new InitializerExpr ({to}), lookup_type (expr)); + auto to = visit_expr (expr.get_to_expr ()); + return_expr (new InitializerExpr ({to}), lookup_type (expr), + expr.get_locus ()); } void @@ -436,10 +490,12 @@ ExprStmtBuilder::visit (HIR::ReturnExpr &ret) if (ret.has_return_expr ()) { push_assignment (RETURN_VALUE_PLACE, - move_place (visit_expr (*ret.get_expr ()))); + move_place (visit_expr (ret.get_expr ()), + ret.get_expr ().get_locus ()), + ret.get_expr ().get_locus ()); } unwind_until (ROOT_SCOPE); - ctx.get_current_bb ().statements.emplace_back (Statement::Kind::RETURN); + push_return (ret.get_locus ()); translated = INVALID_PLACE; } @@ -454,7 +510,7 @@ ExprStmtBuilder::visit (HIR::LoopExpr &expr) { auto loop = setup_loop (expr); - std::ignore = visit_expr (*expr.get_loop_block ()); + std::ignore = visit_expr (expr.get_loop_block ()); if (!ctx.get_current_bb ().is_terminated ()) push_goto (loop.continue_bb); @@ -466,12 +522,12 @@ ExprStmtBuilder::visit (HIR::WhileLoopExpr &expr) { auto loop = setup_loop (expr); - auto cond_val = visit_expr (*expr.get_predicate_expr ()); + auto cond_val = visit_expr (expr.get_predicate_expr ()); auto body_bb = new_bb (); - push_switch (cond_val, {body_bb, loop.break_bb}); + push_switch (cond_val, expr.get_locus (), {body_bb, loop.break_bb}); ctx.current_bb = body_bb; - std::ignore = visit_expr (*expr.get_loop_block ()); + std::ignore = visit_expr (expr.get_loop_block ()); push_goto (loop.continue_bb); ctx.current_bb = loop.break_bb; @@ -489,15 +545,15 @@ ExprStmtBuilder::visit (HIR::IfExpr &expr) { // If without else cannot return a non-unit value (see [E0317]). - if (expr.get_if_block ()->statements.empty ()) + if (expr.get_if_block ().statements.empty ()) return; - push_switch (visit_expr (*expr.get_if_condition ())); + push_switch (visit_expr (expr.get_if_condition ()), expr.get_locus ()); BasicBlockId if_block = ctx.current_bb; ctx.current_bb = new_bb (); BasicBlockId then_start_block = ctx.current_bb; - std::ignore = visit_expr (*expr.get_if_block ()); + std::ignore = visit_expr (expr.get_if_block ()); if (!ctx.get_current_bb ().is_terminated ()) push_goto (INVALID_BB); // Resolved later. BasicBlockId then_end_block = ctx.current_bb; @@ -518,28 +574,30 @@ ExprStmtBuilder::visit (HIR::IfExpr &expr) void ExprStmtBuilder::visit (HIR::IfExprConseqElse &expr) { - push_switch (move_place (visit_expr (*expr.get_if_condition ()))); + push_switch (move_place (visit_expr (expr.get_if_condition ()), + expr.get_if_condition ().get_locus ()), + expr.get_locus ()); BasicBlockId if_end_bb = ctx.current_bb; PlaceId result = take_or_create_return_place (lookup_type (expr)); ctx.current_bb = new_bb (); BasicBlockId then_start_bb = ctx.current_bb; - std::ignore = visit_expr (*expr.get_if_block (), result); + std::ignore = visit_expr (expr.get_if_block (), result); if (!ctx.get_current_bb ().is_terminated ()) push_goto (INVALID_BB); // Resolved later. BasicBlockId then_end_bb = ctx.current_bb; ctx.current_bb = new_bb (); BasicBlockId else_start_bb = ctx.current_bb; - std::ignore = visit_expr (*expr.get_else_block (), result); + std::ignore = visit_expr (expr.get_else_block (), result); if (!ctx.get_current_bb ().is_terminated ()) push_goto (INVALID_BB); // Resolved later. BasicBlockId else_end_bb = ctx.current_bb; ctx.current_bb = new_bb (); BasicBlockId final_start_bb = ctx.current_bb; - return_place (result); + return_place (result, expr.get_locus ()); // Jumps are added at the end to match rustc MIR order for easier comparison. add_jump (if_end_bb, then_start_bb); @@ -555,18 +613,6 @@ ExprStmtBuilder::visit (HIR::IfExprConseqElse &expr) } void -ExprStmtBuilder::visit (HIR::IfLetExpr &expr) -{ - rust_sorry_at (expr.get_locus (), "if let expressions are not supported"); -} - -void -ExprStmtBuilder::visit (HIR::IfLetExprConseqElse &expr) -{ - rust_sorry_at (expr.get_locus (), "if let expressions are not supported"); -} - -void ExprStmtBuilder::visit (HIR::MatchExpr &expr) { rust_sorry_at (expr.get_locus (), "match expressions are not supported"); @@ -627,7 +673,7 @@ ExprStmtBuilder::visit (HIR::QualifiedPathInExpression &expr) { // Note: Type is only stored for the expr, not the segment. PlaceId result = resolve_variable_or_fn (expr, lookup_type (expr)); - return_place (result); + return_place (result, expr.get_locus ()); } void @@ -635,7 +681,7 @@ ExprStmtBuilder::visit (HIR::PathInExpression &expr) { // Note: Type is only stored for the expr, not the segment. PlaceId result = resolve_variable_or_fn (expr, lookup_type (expr)); - return_place (result); + return_place (result, expr.get_locus ()); } void @@ -645,39 +691,39 @@ ExprStmtBuilder::visit (HIR::LetStmt &stmt) tl::optional<TyTy::BaseType *> type_annotation; if (stmt.has_type ()) - type_annotation = lookup_type (*stmt.get_type ()); + type_annotation = lookup_type (stmt.get_type ()); - if (stmt.get_pattern ()->get_pattern_type () == HIR::Pattern::IDENTIFIER) + if (stmt.get_pattern ().get_pattern_type () == HIR::Pattern::IDENTIFIER) { // Only if a pattern is just an identifier, no destructuring is needed. // Hoverer PatternBindingBuilder cannot change existing temporary // (init expr is evaluated before pattern binding) into a // variable, so it would emit extra assignment. - auto var = declare_variable (stmt.get_pattern ()->get_mappings ()); + auto var = declare_variable (stmt.get_pattern ().get_mappings ()); if (stmt.has_type ()) - push_user_type_ascription (var, lookup_type (*stmt.get_type ())); + push_user_type_ascription (var, lookup_type (stmt.get_type ())); if (stmt.has_init_expr ()) - std::ignore = visit_expr (*stmt.get_init_expr (), var); + std::ignore = visit_expr (stmt.get_init_expr (), var); } else { if (stmt.has_init_expr ()) - init = visit_expr (*stmt.get_init_expr ()); + init = visit_expr (stmt.get_init_expr ()); PatternBindingBuilder (ctx, init, type_annotation) - .go (*stmt.get_pattern ()); + .go (stmt.get_pattern ()); } } void ExprStmtBuilder::visit (HIR::ExprStmt &stmt) { - PlaceId result = visit_expr (*stmt.get_expr ()); + PlaceId result = visit_expr (stmt.get_expr ()); // We must read the value for current liveness and we must not store it into // the same place. if (result != INVALID_PLACE) - push_tmp_assignment (result); + push_tmp_assignment (result, stmt.get_locus ()); } } // namespace BIR } // namespace Rust diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.h b/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.h index 3a58611..daedb68 100644 --- a/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.h +++ b/gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.h @@ -99,9 +99,8 @@ protected: // Expr void visit (HIR::WhileLetLoopExpr &expr) override; void visit (HIR::IfExpr &expr) override; void visit (HIR::IfExprConseqElse &expr) override; + void visit (HIR::InlineAsm &expr) override; - void visit (HIR::IfLetExpr &expr) override; - void visit (HIR::IfLetExprConseqElse &expr) override; void visit (HIR::MatchExpr &expr) override; void visit (HIR::AwaitExpr &expr) override; void visit (HIR::AsyncBlockExpr &expr) override; diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-builder-internal.h b/gcc/rust/checks/errors/borrowck/rust-bir-builder-internal.h index f47c41f..4df0e14 100644 --- a/gcc/rust/checks/errors/borrowck/rust-bir-builder-internal.h +++ b/gcc/rust/checks/errors/borrowck/rust-bir-builder-internal.h @@ -76,8 +76,8 @@ struct BuilderContext Resolver::Resolver &resolver; // BIR output - std::vector<BasicBlock> basic_blocks; - size_t current_bb = 0; + BasicBlocks basic_blocks; + BasicBlockId current_bb = ENTRY_BASIC_BLOCK; /** * Allocation and lookup of places (variables, temporaries, paths, and @@ -156,7 +156,7 @@ protected: auto place_id = ctx.place_db.add_variable (nodeid, ty); - if (ctx.place_db.get_current_scope_id () != 0) + if (ctx.place_db.get_current_scope_id () != INVALID_SCOPE) push_storage_live (place_id); if (user_type_annotation) @@ -170,7 +170,7 @@ protected: void pop_scope () { auto &scope = ctx.place_db.get_current_scope (); - if (ctx.place_db.get_current_scope_id () != 0) + if (ctx.place_db.get_current_scope_id () != INVALID_SCOPE) { std::for_each (scope.locals.rbegin (), scope.locals.rend (), [&] (PlaceId place) { push_storage_dead (place); }); @@ -206,7 +206,7 @@ protected: FreeRegions bind_regions (std::vector<TyTy::Region> regions, FreeRegions parent_free_regions) { - std::vector<FreeRegion> free_regions; + FreeRegions free_regions; for (auto ®ion : regions) { if (region.is_early_bound ()) @@ -215,7 +215,7 @@ protected: } else if (region.is_static ()) { - free_regions.push_back (0); + free_regions.push_back (STATIC_FREE_REGION); } else if (region.is_anonymous ()) { @@ -231,87 +231,95 @@ protected: rust_unreachable (); } } - // This is necesarry because of clash of current gcc and gcc4.8. - FreeRegions free_regions_final{std::move (free_regions)}; - return free_regions_final; + return free_regions; } protected: // Helpers to add BIR statements - void push_assignment (PlaceId lhs, AbstractExpr *rhs) + void push_assignment (PlaceId lhs, AbstractExpr *rhs, location_t location) { - ctx.get_current_bb ().statements.emplace_back (lhs, rhs); + ctx.get_current_bb ().statements.push_back ( + Statement::make_assignment (lhs, rhs, location)); translated = lhs; } - void push_assignment (PlaceId lhs, PlaceId rhs) + void push_assignment (PlaceId lhs, PlaceId rhs, location_t location) { - push_assignment (lhs, new Assignment (rhs)); + push_assignment (lhs, new Assignment (rhs), location); } - void push_tmp_assignment (AbstractExpr *rhs, TyTy::BaseType *tyty) + void push_tmp_assignment (AbstractExpr *rhs, TyTy::BaseType *tyty, + location_t location) { PlaceId tmp = ctx.place_db.add_temporary (tyty); push_storage_live (tmp); - push_assignment (tmp, rhs); + push_assignment (tmp, rhs, location); } - void push_tmp_assignment (PlaceId rhs) + void push_tmp_assignment (PlaceId rhs, location_t location) { - push_tmp_assignment (new Assignment (rhs), ctx.place_db[rhs].tyty); + push_tmp_assignment (new Assignment (rhs), ctx.place_db[rhs].tyty, + location); } - void push_switch (PlaceId switch_val, + void push_switch (PlaceId switch_val, location_t location, std::initializer_list<BasicBlockId> destinations = {}) { - auto copy = move_place (switch_val); - ctx.get_current_bb ().statements.emplace_back (Statement::Kind::SWITCH, - copy); + auto copy = move_place (switch_val, location); + ctx.get_current_bb ().statements.push_back (Statement::make_switch (copy)); ctx.get_current_bb ().successors.insert ( ctx.get_current_bb ().successors.end (), destinations); } void push_goto (BasicBlockId bb) { - ctx.get_current_bb ().statements.emplace_back (Statement::Kind::GOTO); + ctx.get_current_bb ().statements.push_back (Statement::make_goto ()); if (bb != INVALID_BB) // INVALID_BB means the goto will be resolved later. ctx.get_current_bb ().successors.push_back (bb); } void push_storage_live (PlaceId place) { - ctx.get_current_bb ().statements.emplace_back ( - Statement::Kind::STORAGE_LIVE, place); + ctx.get_current_bb ().statements.push_back ( + Statement::make_storage_live (place)); } void push_storage_dead (PlaceId place) { - ctx.get_current_bb ().statements.emplace_back ( - Statement::Kind::STORAGE_DEAD, place); + ctx.get_current_bb ().statements.push_back ( + Statement::make_storage_dead (place)); } void push_user_type_ascription (PlaceId place, TyTy::BaseType *ty) { - ctx.get_current_bb ().statements.emplace_back ( - Statement::Kind::USER_TYPE_ASCRIPTION, place, ty); + ctx.get_current_bb ().statements.push_back ( + Statement::make_user_type_ascription (place, ty)); } void push_fake_read (PlaceId place) { - ctx.get_current_bb ().statements.emplace_back (Statement::Kind::FAKE_READ, - place); + ctx.get_current_bb ().statements.push_back ( + Statement::make_fake_read (place)); } - PlaceId borrow_place (PlaceId place_id, TyTy::BaseType *ty) + void push_return (location_t location) + { + ctx.get_current_bb ().statements.push_back ( + Statement::make_return (location)); + } + + PlaceId borrow_place (PlaceId place_id, TyTy::BaseType *ty, + location_t location) { auto mutability = ty->as<const TyTy::ReferenceType> ()->mutability (); - auto loan = ctx.place_db.add_loan ({mutability, place_id}); - push_tmp_assignment (new BorrowExpr (place_id, loan, - ctx.place_db.get_next_free_region ()), - ty); + auto loan = ctx.place_db.add_loan ({mutability, place_id, location}); + push_tmp_assignment ( + new BorrowExpr (place_id, loan, + ctx.place_db.get_next_free_region ().value), + ty, location); return translated; } - PlaceId move_place (PlaceId arg) + PlaceId move_place (PlaceId arg, location_t location) { auto &place = ctx.place_db[arg]; @@ -319,34 +327,38 @@ protected: // Helpers to add BIR statements return arg; if (place.tyty->is<TyTy::ReferenceType> ()) - return reborrow_place (arg); + return reborrow_place (arg, location); if (place.is_rvalue ()) return arg; - push_tmp_assignment (arg); + push_tmp_assignment (arg, location); return translated; } - PlaceId reborrow_place (PlaceId arg) + PlaceId reborrow_place (PlaceId arg, location_t location) { auto ty = ctx.place_db[arg].tyty->as<TyTy::ReferenceType> (); return borrow_place (ctx.place_db.lookup_or_add_path (Place::DEREF, ty->get_base (), arg), - ty); + ty, location); } - template <typename T> void move_all (T &args) + template <typename T> + void move_all (T &args, std::vector<location_t> locations) { - std::transform (args.begin (), args.end (), args.begin (), - [this] (PlaceId arg) { return move_place (arg); }); + rust_assert (args.size () == locations.size ()); + std::transform (args.begin (), args.end (), locations.begin (), + args.begin (), [this] (PlaceId arg, location_t location) { + return move_place (arg, location); + }); } protected: // CFG helpers BasicBlockId new_bb () { ctx.basic_blocks.emplace_back (); - return ctx.basic_blocks.size () - 1; + return {ctx.basic_blocks.size () - 1}; } BasicBlockId start_new_consecutive_bb () @@ -477,12 +489,15 @@ protected: // Implicit conversions. { if (ctx.place_db[translated].tyty->get_kind () != TyTy::REF) { + // FIXME: not sure how to fetch correct location for this + // this function is unused yet, so can ignore for now auto ty = ctx.place_db[translated].tyty; translated = borrow_place (translated, new TyTy::ReferenceType (ty->get_ref (), TyTy::TyVar (ty->get_ref ()), - Mutability::Imm)); + Mutability::Imm), + UNKNOWN_LOCATION); } } }; @@ -531,16 +546,16 @@ protected: * @param can_panic mark that expression can panic to insert jump to * cleanup. */ - void return_expr (AbstractExpr *expr, TyTy::BaseType *ty, + void return_expr (AbstractExpr *expr, TyTy::BaseType *ty, location_t location, bool can_panic = false) { if (expr_return_place != INVALID_PLACE) { - push_assignment (expr_return_place, expr); + push_assignment (expr_return_place, expr, location); } else { - push_tmp_assignment (expr, ty); + push_tmp_assignment (expr, ty, location); } if (can_panic) @@ -556,12 +571,12 @@ protected: } /** Mark place to be a result of processed subexpression. */ - void return_place (PlaceId place, bool can_panic = false) + void return_place (PlaceId place, location_t location, bool can_panic = false) { if (expr_return_place != INVALID_PLACE) { // Return place is already allocated, no need to defer assignment. - push_assignment (expr_return_place, place); + push_assignment (expr_return_place, place, location); } else { @@ -585,14 +600,16 @@ protected: translated = ctx.place_db.get_constant (lookup_type (expr)); } - PlaceId return_borrowed (PlaceId place_id, TyTy::BaseType *ty) + PlaceId return_borrowed (PlaceId place_id, TyTy::BaseType *ty, + location_t location) { // TODO: deduplicate with borrow_place auto loan = ctx.place_db.add_loan ( - {ty->as<const TyTy::ReferenceType> ()->mutability (), place_id}); + {ty->as<const TyTy::ReferenceType> ()->mutability (), place_id, + location}); return_expr (new BorrowExpr (place_id, loan, - ctx.place_db.get_next_free_region ()), - ty); + ctx.place_db.get_next_free_region ().value), + ty, location); return translated; } diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-builder-lazyboolexpr.h b/gcc/rust/checks/errors/borrowck/rust-bir-builder-lazyboolexpr.h index 11134c2..3bc622c 100644 --- a/gcc/rust/checks/errors/borrowck/rust-bir-builder-lazyboolexpr.h +++ b/gcc/rust/checks/errors/borrowck/rust-bir-builder-lazyboolexpr.h @@ -37,7 +37,8 @@ class LazyBooleanExprBuilder : public AbstractExprBuilder public: explicit LazyBooleanExprBuilder (BuilderContext &ctx, PlaceId expr_return_place = INVALID_PLACE) - : AbstractExprBuilder (ctx, expr_return_place), short_circuit_bb (0) + : AbstractExprBuilder (ctx, expr_return_place), + short_circuit_bb (ENTRY_BASIC_BLOCK) {} PlaceId build (HIR::LazyBooleanExpr &expr) @@ -45,13 +46,14 @@ public: PlaceId return_place = take_or_create_return_place (lookup_type (expr)); short_circuit_bb = new_bb (); - push_assignment (return_place, visit_expr (expr)); + push_assignment (return_place, visit_expr (expr), expr.get_locus ()); auto final_bb = new_bb (); push_goto (final_bb); ctx.current_bb = short_circuit_bb; push_assignment (return_place, - ctx.place_db.get_constant (lookup_type (expr))); + ctx.place_db.get_constant (lookup_type (expr)), + expr.get_locus ()); push_goto (final_bb); ctx.current_bb = final_bb; @@ -61,30 +63,31 @@ public: protected: void visit (HIR::LazyBooleanExpr &expr) override { - auto lhs = visit_expr (*expr.get_lhs ()); - push_switch (move_place (lhs), {short_circuit_bb}); + auto lhs = visit_expr (expr.get_lhs ()); + push_switch (move_place (lhs, expr.get_lhs ().get_locus ()), + expr.get_locus (), {short_circuit_bb}); start_new_consecutive_bb (); - return_place (visit_expr (*expr.get_rhs ())); + return_place (visit_expr (expr.get_rhs ()), expr.get_locus ()); } void visit (HIR::GroupedExpr &expr) override { - expr.get_expr_in_parens ()->accept_vis (*this); + expr.get_expr_in_parens ().accept_vis (*this); } protected: public: void visit (HIR::QualifiedPathInExpression &expr) override { - return_place (ExprStmtBuilder (ctx).build (expr)); + return_place (ExprStmtBuilder (ctx).build (expr), expr.get_locus ()); } void visit (HIR::PathInExpression &expr) override { - return_place (ExprStmtBuilder (ctx).build (expr)); + return_place (ExprStmtBuilder (ctx).build (expr), expr.get_locus ()); } void visit (HIR::ClosureExpr &expr) override { - return_place (ExprStmtBuilder (ctx).build (expr)); + return_place (ExprStmtBuilder (ctx).build (expr), expr.get_locus ()); } void visit (HIR::StructExprStructFields &fields) override { @@ -96,121 +99,115 @@ public: } void visit (HIR::LiteralExpr &expr) override { - return_place (ExprStmtBuilder (ctx).build (expr)); + return_place (ExprStmtBuilder (ctx).build (expr), expr.get_locus ()); } void visit (HIR::BorrowExpr &expr) override { - return_place (ExprStmtBuilder (ctx).build (expr)); + return_place (ExprStmtBuilder (ctx).build (expr), expr.get_locus ()); } void visit (HIR::DereferenceExpr &expr) override { - return_place (ExprStmtBuilder (ctx).build (expr)); + return_place (ExprStmtBuilder (ctx).build (expr), expr.get_locus ()); } void visit (HIR::ErrorPropagationExpr &expr) override { - return_place (ExprStmtBuilder (ctx).build (expr)); + return_place (ExprStmtBuilder (ctx).build (expr), expr.get_locus ()); } void visit (HIR::NegationExpr &expr) override { - return_place (ExprStmtBuilder (ctx).build (expr)); + return_place (ExprStmtBuilder (ctx).build (expr), expr.get_locus ()); } void visit (HIR::ArithmeticOrLogicalExpr &expr) override { - return_place (ExprStmtBuilder (ctx).build (expr)); + return_place (ExprStmtBuilder (ctx).build (expr), expr.get_locus ()); } void visit (HIR::ComparisonExpr &expr) override { - return_place (ExprStmtBuilder (ctx).build (expr)); + return_place (ExprStmtBuilder (ctx).build (expr), expr.get_locus ()); } void visit (HIR::TypeCastExpr &expr) override { - return_place (ExprStmtBuilder (ctx).build (expr)); + return_place (ExprStmtBuilder (ctx).build (expr), expr.get_locus ()); } void visit (HIR::AssignmentExpr &expr) override { - return_place (ExprStmtBuilder (ctx).build (expr)); + return_place (ExprStmtBuilder (ctx).build (expr), expr.get_locus ()); } void visit (HIR::CompoundAssignmentExpr &expr) override { - return_place (ExprStmtBuilder (ctx).build (expr)); + return_place (ExprStmtBuilder (ctx).build (expr), expr.get_locus ()); } void visit (HIR::ArrayExpr &expr) override { - return_place (ExprStmtBuilder (ctx).build (expr)); + return_place (ExprStmtBuilder (ctx).build (expr), expr.get_locus ()); } void visit (HIR::ArrayIndexExpr &expr) override { - return_place (ExprStmtBuilder (ctx).build (expr)); + return_place (ExprStmtBuilder (ctx).build (expr), expr.get_locus ()); } void visit (HIR::TupleExpr &expr) override { - return_place (ExprStmtBuilder (ctx).build (expr)); + return_place (ExprStmtBuilder (ctx).build (expr), expr.get_locus ()); } void visit (HIR::TupleIndexExpr &expr) override { - return_place (ExprStmtBuilder (ctx).build (expr)); + return_place (ExprStmtBuilder (ctx).build (expr), expr.get_locus ()); } void visit (HIR::CallExpr &expr) override { - return_place (ExprStmtBuilder (ctx).build (expr)); + return_place (ExprStmtBuilder (ctx).build (expr), expr.get_locus ()); } void visit (HIR::MethodCallExpr &expr) override { - return_place (ExprStmtBuilder (ctx).build (expr)); + return_place (ExprStmtBuilder (ctx).build (expr), expr.get_locus ()); } void visit (HIR::FieldAccessExpr &expr) override { - return_place (ExprStmtBuilder (ctx).build (expr)); + return_place (ExprStmtBuilder (ctx).build (expr), expr.get_locus ()); } void visit (HIR::BlockExpr &expr) override { - return_place (ExprStmtBuilder (ctx).build (expr)); + return_place (ExprStmtBuilder (ctx).build (expr), expr.get_locus ()); } void visit (HIR::UnsafeBlockExpr &expr) override { - return_place (ExprStmtBuilder (ctx).build (expr)); + return_place (ExprStmtBuilder (ctx).build (expr), expr.get_locus ()); } void visit (HIR::LoopExpr &expr) override { - return_place (ExprStmtBuilder (ctx).build (expr)); + return_place (ExprStmtBuilder (ctx).build (expr), expr.get_locus ()); } void visit (HIR::WhileLoopExpr &expr) override { - return_place (ExprStmtBuilder (ctx).build (expr)); + return_place (ExprStmtBuilder (ctx).build (expr), expr.get_locus ()); } void visit (HIR::WhileLetLoopExpr &expr) override { - return_place (ExprStmtBuilder (ctx).build (expr)); + return_place (ExprStmtBuilder (ctx).build (expr), expr.get_locus ()); } void visit (HIR::IfExpr &expr) override { - return_place (ExprStmtBuilder (ctx).build (expr)); + return_place (ExprStmtBuilder (ctx).build (expr), expr.get_locus ()); } void visit (HIR::IfExprConseqElse &expr) override { - return_place (ExprStmtBuilder (ctx).build (expr)); - } - void visit (HIR::IfLetExpr &expr) override - { - return_place (ExprStmtBuilder (ctx).build (expr)); - } - void visit (HIR::IfLetExprConseqElse &expr) override - { - return_place (ExprStmtBuilder (ctx).build (expr)); + return_place (ExprStmtBuilder (ctx).build (expr), expr.get_locus ()); } void visit (HIR::MatchExpr &expr) override { - return_place (ExprStmtBuilder (ctx).build (expr)); + return_place (ExprStmtBuilder (ctx).build (expr), expr.get_locus ()); } void visit (HIR::AwaitExpr &expr) override { - return_place (ExprStmtBuilder (ctx).build (expr)); + return_place (ExprStmtBuilder (ctx).build (expr), expr.get_locus ()); } void visit (HIR::AsyncBlockExpr &expr) override { - return_place (ExprStmtBuilder (ctx).build (expr)); + return_place (ExprStmtBuilder (ctx).build (expr), expr.get_locus ()); } + void visit (HIR::InlineAsm &expr) override {} + protected: // Illegal at this position. void visit (HIR::StructExprFieldIdentifier &field) override { diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-builder-pattern.cc b/gcc/rust/checks/errors/borrowck/rust-bir-builder-pattern.cc new file mode 100644 index 0000000..ee37bb0 --- /dev/null +++ b/gcc/rust/checks/errors/borrowck/rust-bir-builder-pattern.cc @@ -0,0 +1,273 @@ +#include "rust-bir-builder-pattern.h" + +namespace Rust { +namespace BIR { + +void +PatternBindingBuilder::visit_identifier (const Analysis::NodeMapping &node, + bool is_ref, location_t location, + bool is_mut) +{ + if (is_ref) + { + translated = declare_variable ( + node, + new TyTy::ReferenceType (node.get_hirid (), + TyTy::TyVar (node.get_hirid ()), + (is_mut) ? Mutability::Mut : Mutability::Imm)); + } + else + { + translated = declare_variable (node); + } + + if (init.has_value ()) + { + push_assignment (translated, init.value (), location); + } +} + +void +PatternBindingBuilder::visit (HIR::IdentifierPattern &pattern) +{ + // Top-level identifiers are resolved directly to avoid useless temporary + // (for cleaner BIR). + visit_identifier (pattern.get_mappings (), pattern.get_is_ref (), + pattern.get_locus (), pattern.is_mut ()); +} + +void +PatternBindingBuilder::visit (HIR::ReferencePattern &pattern) +{ + SavedState saved (this); + + init = init.map ([&] (PlaceId id) { + return ctx.place_db.lookup_or_add_path (Place::DEREF, lookup_type (pattern), + id); + }); + + type_annotation = type_annotation.map ([&] (TyTy::BaseType *ty) { + return ty->as<TyTy::ReferenceType> ()->get_base (); + }); + + pattern.get_referenced_pattern ().accept_vis (*this); +} + +void +PatternBindingBuilder::visit (HIR::SlicePattern &pattern) +{ + SavedState saved (this); + + // All indexes are supposed to point to the same place for borrow-checking. + // init = ctx.place_db.lookup_or_add_path (Place::INDEX, lookup_type + // (pattern), saved.init); + init = init.map ([&] (PlaceId id) { + return ctx.place_db.lookup_or_add_path (Place::INDEX, lookup_type (pattern), + id); + }); + + type_annotation = type_annotation.map ([&] (TyTy::BaseType *ty) { + return ty->as<TyTy::SliceType> ()->get_element_type (); + }); + + // Regions are unchnaged. + + for (auto &item : pattern.get_items ()) + { + item->accept_vis (*this); + } +} + +void +PatternBindingBuilder::visit (HIR::AltPattern &pattern) +{ + rust_sorry_at (pattern.get_locus (), + "borrow-checking of alt patterns is not yet implemented"); +} + +void +PatternBindingBuilder::visit (HIR::StructPattern &pattern) +{ + SavedState saved (this); + + auto tyty = ctx.place_db[init.value ()].tyty; + rust_assert (tyty->get_kind () == TyTy::ADT); + auto adt_ty = static_cast<TyTy::ADTType *> (tyty); + rust_assert (adt_ty->is_struct_struct ()); + auto struct_ty = adt_ty->get_variants ().at (0); + + for (auto &field : + pattern.get_struct_pattern_elems ().get_struct_pattern_fields ()) + { + switch (field->get_item_type ()) + { + case HIR::StructPatternField::TUPLE_PAT: { + auto tuple + = static_cast<HIR::StructPatternFieldTuplePat *> (field.get ()); + + init = init.map ([&] (PlaceId id) { + return ctx.place_db.lookup_or_add_path ( + Place::FIELD, lookup_type (tuple->get_tuple_pattern ()), id, + tuple->get_index ()); + }); + + type_annotation = type_annotation.map ([&] (TyTy::BaseType *ty) { + return ty->as<TyTy::ADTType> () + ->get_variants () + .at (0) + ->get_fields () + .at (tuple->get_index ()) + ->get_field_type (); + }); + + tuple->get_tuple_pattern ().accept_vis (*this); + break; + } + case HIR::StructPatternField::IDENT_PAT: { + auto ident_field + = static_cast<HIR::StructPatternFieldIdentPat *> (field.get ()); + TyTy::StructFieldType *field_ty = nullptr; + size_t field_index = 0; + auto ok = struct_ty->lookup_field ( + ident_field->get_identifier ().as_string (), &field_ty, + &field_index); + rust_assert (ok); + init = ctx.place_db.lookup_or_add_path (Place::FIELD, + field_ty->get_field_type (), + saved.init.value (), + field_index); + ident_field->get_pattern ().accept_vis (*this); + break; + } + case HIR::StructPatternField::IDENT: { + auto ident_field + = static_cast<HIR::StructPatternFieldIdent *> (field.get ()); + TyTy::StructFieldType *field_ty = nullptr; + size_t field_index = 0; + auto ok = struct_ty->lookup_field ( + ident_field->get_identifier ().as_string (), &field_ty, + &field_index); + rust_assert (ok); + init = ctx.place_db.lookup_or_add_path (Place::FIELD, + field_ty->get_field_type (), + saved.init.value (), + field_index); + visit_identifier (ident_field->get_mappings (), + ident_field->get_has_ref (), + ident_field->get_locus (), + ident_field->is_mut ()); + break; + } + } + } +} + +void +PatternBindingBuilder::visit_tuple_fields ( + std::vector<std::unique_ptr<HIR::Pattern>> &fields, SavedState &saved, + size_t &index) +{ + for (auto &item : fields) + { + auto type = lookup_type (*item); + + init = init.map ([&] (PlaceId id) { + return ctx.place_db.lookup_or_add_path (Place::FIELD, type, id, index); + }); + + type_annotation = type_annotation.map ([&] (TyTy::BaseType *ty) { + return ty->as<TyTy::TupleType> ()->get_fields ().at (index).get_tyty (); + }); + + regions = regions.map ([&] (FreeRegions regs) { + return bind_regions (Resolver::TypeCheckContext::get () + ->get_variance_analysis_ctx () + .query_type_regions (type), + regs); + }); + + item->accept_vis (*this); + index++; + } +} + +void +PatternBindingBuilder::visit (HIR::TuplePattern &pattern) +{ + SavedState saved (this); + + size_t index = 0; + switch (pattern.get_items ().get_item_type ()) + { + case HIR::TuplePatternItems::MULTIPLE: { + auto &items = static_cast<HIR::TuplePatternItemsMultiple &> ( + pattern.get_items ()); + visit_tuple_fields (items.get_patterns (), saved, index); + break; + } + case HIR::TuplePatternItems::RANGED: { + auto &items + = static_cast<HIR::TuplePatternItemsRanged &> (pattern.get_items ()); + + auto tyty = ctx.place_db[init.value ()].tyty; + rust_assert (tyty->get_kind () == TyTy::TUPLE); + + auto skipped = (static_cast<TyTy::TupleType *> (tyty))->num_fields () + - items.get_lower_patterns ().size () + - items.get_upper_patterns ().size (); + + visit_tuple_fields (items.get_lower_patterns (), saved, index); + index += skipped; + visit_tuple_fields (items.get_upper_patterns (), saved, index); + break; + } + } + init = saved.init; +} + +void +PatternBindingBuilder::visit (HIR::TupleStructPattern &pattern) +{ + SavedState saved (this); + + type_annotation = tl::nullopt; + + auto type = lookup_type (pattern); + + regions = regions.map ([&] (FreeRegions regs) { + return bind_regions (Resolver::TypeCheckContext::get () + ->get_variance_analysis_ctx () + .query_type_regions (type), + regs); + }); + + size_t index = 0; + switch (pattern.get_items ().get_item_type ()) + { + case HIR::TupleStructItems::RANGED: { + auto &items + = static_cast<HIR::TupleStructItemsRange &> (pattern.get_items ()); + + rust_assert (type->get_kind () == TyTy::ADT); + auto adt_ty = static_cast<TyTy::ADTType *> (type); + rust_assert (adt_ty->is_tuple_struct ()); + + auto skipped = adt_ty->get_variants ().at (0)->get_fields ().size () + - items.get_lower_patterns ().size () + - items.get_upper_patterns ().size (); + + visit_tuple_fields (items.get_lower_patterns (), saved, index); + index += skipped; + visit_tuple_fields (items.get_upper_patterns (), saved, index); + break; + } + case HIR::TupleStructItems::MULTIPLE: { + auto &items + = static_cast<HIR::TupleStructItemsNoRange &> (pattern.get_items ()); + visit_tuple_fields (items.get_patterns (), saved, index); + break; + } + } +} +} // namespace BIR +} // namespace Rust diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-builder-pattern.h b/gcc/rust/checks/errors/borrowck/rust-bir-builder-pattern.h index f704f77..33ecd23 100644 --- a/gcc/rust/checks/errors/borrowck/rust-bir-builder-pattern.h +++ b/gcc/rust/checks/errors/borrowck/rust-bir-builder-pattern.h @@ -65,265 +65,24 @@ public: void go (HIR::Pattern &pattern) { pattern.accept_vis (*this); } void visit_identifier (const Analysis::NodeMapping &node, bool is_ref, - bool is_mut = false) - { - if (is_ref) - { - translated = declare_variable ( - node, new TyTy::ReferenceType (node.get_hirid (), - TyTy::TyVar (node.get_hirid ()), - (is_mut) ? Mutability::Mut - : Mutability::Imm)); - } - else - { - translated = declare_variable (node); - } - - if (init.has_value ()) - { - push_assignment (translated, init.value ()); - } - } - - void visit (HIR::IdentifierPattern &pattern) override - { - // Top-level identifiers are resolved directly to avoid useless temporary - // (for cleaner BIR). - visit_identifier (pattern.get_mappings (), pattern.get_is_ref (), - pattern.is_mut ()); - } - - void visit (HIR::ReferencePattern &pattern) override - { - SavedState saved (this); - - init = init.map ([&] (PlaceId id) { - return ctx.place_db.lookup_or_add_path (Place::DEREF, - lookup_type (pattern), id); - }); - - type_annotation = type_annotation.map ([&] (TyTy::BaseType *ty) { - return ty->as<TyTy::ReferenceType> ()->get_base (); - }); - - pattern.get_referenced_pattern ()->accept_vis (*this); - } - - void visit (HIR::SlicePattern &pattern) override - { - SavedState saved (this); - - // All indexes are supposed to point to the same place for borrow-checking. - // init = ctx.place_db.lookup_or_add_path (Place::INDEX, lookup_type - // (pattern), saved.init); - init = init.map ([&] (PlaceId id) { - return ctx.place_db.lookup_or_add_path (Place::INDEX, - lookup_type (pattern), id); - }); - - type_annotation = type_annotation.map ([&] (TyTy::BaseType *ty) { - return ty->as<TyTy::SliceType> ()->get_element_type (); - }); - - // Regions are unchnaged. - - for (auto &item : pattern.get_items ()) - { - item->accept_vis (*this); - } - } - - void visit (HIR::AltPattern &pattern) override - { - rust_sorry_at (pattern.get_locus (), - "borrow-checking of alt patterns is not yet implemented"); - } - - void visit (HIR::StructPattern &pattern) override - { - SavedState saved (this); + location_t location, bool is_mut = false); - auto tyty = ctx.place_db[init.value ()].tyty; - rust_assert (tyty->get_kind () == TyTy::ADT); - auto adt_ty = static_cast<TyTy::ADTType *> (tyty); - rust_assert (adt_ty->is_struct_struct ()); - auto struct_ty = adt_ty->get_variants ().at (0); + void visit (HIR::IdentifierPattern &pattern) override; - for (auto &field : - pattern.get_struct_pattern_elems ().get_struct_pattern_fields ()) - { - switch (field->get_item_type ()) - { - case HIR::StructPatternField::TUPLE_PAT: { - auto tuple - = static_cast<HIR::StructPatternFieldTuplePat *> (field.get ()); + void visit (HIR::ReferencePattern &pattern) override; - init = init.map ([&] (PlaceId id) { - return ctx.place_db.lookup_or_add_path ( - Place::FIELD, lookup_type (*tuple->get_tuple_pattern ()), id, - tuple->get_index ()); - }); + void visit (HIR::SlicePattern &pattern) override; - type_annotation = type_annotation.map ([&] (TyTy::BaseType *ty) { - return ty->as<TyTy::ADTType> () - ->get_variants () - .at (0) - ->get_fields () - .at (tuple->get_index ()) - ->get_field_type (); - }); + void visit (HIR::AltPattern &pattern) override; - tuple->get_tuple_pattern ()->accept_vis (*this); - break; - } - case HIR::StructPatternField::IDENT_PAT: { - auto ident_field - = static_cast<HIR::StructPatternFieldIdentPat *> (field.get ()); - TyTy::StructFieldType *field_ty = nullptr; - size_t field_index = 0; - auto ok = struct_ty->lookup_field ( - ident_field->get_identifier ().as_string (), &field_ty, - &field_index); - rust_assert (ok); - init - = ctx.place_db.lookup_or_add_path (Place::FIELD, - field_ty->get_field_type (), - saved.init.value (), - field_index); - ident_field->get_pattern ()->accept_vis (*this); - break; - } - case HIR::StructPatternField::IDENT: { - auto ident_field - = static_cast<HIR::StructPatternFieldIdent *> (field.get ()); - TyTy::StructFieldType *field_ty = nullptr; - size_t field_index = 0; - auto ok = struct_ty->lookup_field ( - ident_field->get_identifier ().as_string (), &field_ty, - &field_index); - rust_assert (ok); - init - = ctx.place_db.lookup_or_add_path (Place::FIELD, - field_ty->get_field_type (), - saved.init.value (), - field_index); - visit_identifier (ident_field->get_mappings (), - ident_field->get_has_ref (), - ident_field->is_mut ()); - break; - } - } - } - } + void visit (HIR::StructPattern &pattern) override; void visit_tuple_fields (std::vector<std::unique_ptr<HIR::Pattern>> &fields, - SavedState &saved, size_t &index) - { - for (auto &item : fields) - { - auto type = lookup_type (*item); - - init = init.map ([&] (PlaceId id) { - return ctx.place_db.lookup_or_add_path (Place::FIELD, type, id, - index); - }); - - type_annotation = type_annotation.map ([&] (TyTy::BaseType *ty) { - return ty->as<TyTy::TupleType> () - ->get_fields () - .at (index) - .get_tyty (); - }); - - regions = regions.map ([&] (FreeRegions regs) { - return bind_regions (Resolver::TypeCheckContext::get () - ->get_variance_analysis_ctx () - .query_type_regions (type), - regs); - }); - - item->accept_vis (*this); - index++; - } - } - - void visit (HIR::TuplePattern &pattern) override - { - SavedState saved (this); - - size_t index = 0; - switch (pattern.get_items ()->get_item_type ()) - { - case HIR::TuplePatternItems::MULTIPLE: { - auto &items = static_cast<HIR::TuplePatternItemsMultiple &> ( - *pattern.get_items ()); - visit_tuple_fields (items.get_patterns (), saved, index); - break; - } - case HIR::TuplePatternItems::RANGED: { - auto &items = static_cast<HIR::TuplePatternItemsRanged &> ( - *pattern.get_items ()); - - auto tyty = ctx.place_db[init.value ()].tyty; - rust_assert (tyty->get_kind () == TyTy::TUPLE); - - auto skipped = (static_cast<TyTy::TupleType *> (tyty))->num_fields () - - items.get_lower_patterns ().size () - - items.get_upper_patterns ().size (); + SavedState &saved, size_t &index); - visit_tuple_fields (items.get_lower_patterns (), saved, index); - index += skipped; - visit_tuple_fields (items.get_upper_patterns (), saved, index); - break; - } - } - init = saved.init; - } + void visit (HIR::TuplePattern &pattern) override; - void visit (HIR::TupleStructPattern &pattern) override - { - SavedState saved (this); - - type_annotation = tl::nullopt; - - auto type = lookup_type (pattern); - - regions = regions.map ([&] (FreeRegions regs) { - return bind_regions (Resolver::TypeCheckContext::get () - ->get_variance_analysis_ctx () - .query_type_regions (type), - regs); - }); - - size_t index = 0; - switch (pattern.get_items ()->get_item_type ()) - { - case HIR::TupleStructItems::RANGED: { - auto &items - = static_cast<HIR::TupleStructItemsRange &> (*pattern.get_items ()); - - rust_assert (type->get_kind () == TyTy::ADT); - auto adt_ty = static_cast<TyTy::ADTType *> (type); - rust_assert (adt_ty->is_tuple_struct ()); - - auto skipped = adt_ty->get_variants ().at (0)->get_fields ().size () - - items.get_lower_patterns ().size () - - items.get_upper_patterns ().size (); - - visit_tuple_fields (items.get_lower_patterns (), saved, index); - index += skipped; - visit_tuple_fields (items.get_upper_patterns (), saved, index); - break; - } - case HIR::TupleStructItems::MULTIPLE: { - auto &items = static_cast<HIR::TupleStructItemsNoRange &> ( - *pattern.get_items ()); - visit_tuple_fields (items.get_patterns (), saved, index); - break; - } - } - } + void visit (HIR::TupleStructPattern &pattern) override; void visit (HIR::WildcardPattern &pattern) override {} // Unused for binding. @@ -332,6 +91,7 @@ public: void visit (HIR::QualifiedPathInExpression &expression) override {} void visit (HIR::RangePattern &pattern) override {} }; + } // namespace BIR } // namespace Rust diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-builder-struct.h b/gcc/rust/checks/errors/borrowck/rust-bir-builder-struct.h index 53346bf..18ddc19 100644 --- a/gcc/rust/checks/errors/borrowck/rust-bir-builder-struct.h +++ b/gcc/rust/checks/errors/borrowck/rust-bir-builder-struct.h @@ -52,12 +52,12 @@ public: } void visit (HIR::StructExprFieldIdentifierValue &field) override { - auto value = ExprStmtBuilder (ctx).build (*field.get_value ()); + auto value = ExprStmtBuilder (ctx).build (field.get_value ()); handle_named_field (field, value); } void visit (HIR::StructExprFieldIndexValue &field) override { - auto value = ExprStmtBuilder (ctx).build (*field.get_value ()); + auto value = ExprStmtBuilder (ctx).build (field.get_value ()); coercion_site (value, struct_ty->get_field_at_index (field.get_tuple_index ()) ->get_field_type ()); @@ -149,8 +149,6 @@ protected: void visit (HIR::WhileLetLoopExpr &expr) override { rust_unreachable (); } void visit (HIR::IfExpr &expr) override { rust_unreachable (); } void visit (HIR::IfExprConseqElse &expr) override { rust_unreachable (); } - void visit (HIR::IfLetExpr &expr) override { rust_unreachable (); } - void visit (HIR::IfLetExprConseqElse &expr) override { rust_unreachable (); } void visit (HIR::MatchExpr &expr) override { rust_unreachable (); } void visit (HIR::AwaitExpr &expr) override { rust_unreachable (); } void visit (HIR::AsyncBlockExpr &expr) override { rust_unreachable (); } diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-builder.h b/gcc/rust/checks/errors/borrowck/rust-bir-builder.h index bafa22b4..e5bdb46 100644 --- a/gcc/rust/checks/errors/borrowck/rust-bir-builder.h +++ b/gcc/rust/checks/errors/borrowck/rust-bir-builder.h @@ -49,7 +49,9 @@ public: for (auto ¶m : function.get_function_params ()) handle_param (param); - handle_body (*function.get_definition ()); + handle_body (function.get_definition ()); + auto region_hir_map + = map_region_to_hir (function.get_generic_params (), ctx.fn_free_regions); return Function{ std::move (ctx.place_db), @@ -57,6 +59,7 @@ public: std::move (ctx.basic_blocks), std::move (ctx.fn_free_regions), std::move (universal_region_bounds), + std::move (region_hir_map), function.get_locus (), }; } @@ -65,15 +68,15 @@ private: /** Instantiate `num_lifetime_params` free regions. */ void handle_lifetime_params (size_t num_lifetime_params) { - std::vector<FreeRegion> function_free_regions; + FreeRegions regions; for (size_t i = 0; i < num_lifetime_params; i++) { - function_free_regions.push_back (ctx.place_db.get_next_free_region ()); + regions.push_back (ctx.place_db.get_next_free_region ()); } rust_debug ("\tctx.fn_free_region={%s}", ctx.fn_free_regions.to_string ().c_str ()); - ctx.fn_free_regions.set_from (std::move (function_free_regions)); + ctx.fn_free_regions = regions; } void handle_lifetime_param_constraints ( @@ -91,8 +94,8 @@ private: ctx.fn_free_regions[bound.second.get_index ()]); auto last_bound = universal_region_bounds.back (); - rust_debug ("\t\t %lu: %lu", (unsigned long) last_bound.first, - (unsigned long) last_bound.second); + rust_debug ("\t\t %lu: %lu", (unsigned long) last_bound.first.value, + (unsigned long) last_bound.second.value); } // TODO: handle type_region constraints @@ -115,14 +118,14 @@ private: void handle_param (HIR::FunctionParam ¶m) { - auto param_type = lookup_type (*param.get_param_name ()); + auto param_type = lookup_type (param.get_param_name ()); auto &pattern = param.get_param_name (); - if (pattern->get_pattern_type () == HIR::Pattern::IDENTIFIER - && !static_cast<HIR::IdentifierPattern &> (*pattern).get_is_ref ()) + if (pattern.get_pattern_type () == HIR::Pattern::IDENTIFIER + && !static_cast<HIR::IdentifierPattern &> (pattern).get_is_ref ()) { // Avoid useless temporary variable for parameter to look like MIR. - translated = declare_variable (pattern->get_mappings ()); + translated = declare_variable (pattern.get_mappings ()); ctx.arguments.push_back (translated); } else @@ -130,11 +133,9 @@ private: translated = ctx.place_db.add_temporary (param_type); ctx.arguments.push_back (translated); PatternBindingBuilder (ctx, translated, tl::nullopt) - .go (*param.get_param_name ()); + .go (param.get_param_name ()); } - rust_assert (param.get_type () != nullptr); - // Set parameter place to use functions regions, not the fresh ones. ctx.place_db[translated].regions = bind_regions (Resolver::TypeCheckContext::get () @@ -152,10 +153,34 @@ private: { push_assignment (RETURN_VALUE_PLACE, ctx.place_db.get_constant ( - ctx.place_db[RETURN_VALUE_PLACE].tyty)); + ctx.place_db[RETURN_VALUE_PLACE].tyty), + body.get_end_locus ()); + } + auto return_location = body.has_expr () + ? body.get_final_expr ().get_locus () + : body.get_end_locus (); + push_return (return_location); + } + } + + // Maps named lifetime parameters to their respective HIR node + const std::unordered_map<Polonius::Origin, HIR::LifetimeParam *> + map_region_to_hir ( + const std::vector<std::unique_ptr<HIR::GenericParam>> &generic_params, + const FreeRegions ®ions) + { + std::unordered_map<Polonius::Origin, HIR::LifetimeParam *> result; + size_t region_index = 0; + for (auto &generic_param : generic_params) + { + if (generic_param->get_kind () + == HIR::GenericParam::GenericKind::LIFETIME) + { + result[regions[region_index++].value] + = static_cast<HIR::LifetimeParam *> (generic_param.get ()); } - ctx.get_current_bb ().statements.emplace_back (Statement::Kind::RETURN); } + return result; } }; diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-dump.cc b/gcc/rust/checks/errors/borrowck/rust-bir-dump.cc index a35f47b..3864b81 100644 --- a/gcc/rust/checks/errors/borrowck/rust-bir-dump.cc +++ b/gcc/rust/checks/errors/borrowck/rust-bir-dump.cc @@ -53,21 +53,25 @@ renumber_places (const Function &func, std::vector<PlaceId> &place_map) { // Renumbering places to avoid gaps in the place id space. // This is needed to match MIR's shape. - size_t next_out_id = 0; + PlaceId next_out_id = INVALID_PLACE; - for (size_t in_id = FIRST_VARIABLE_PLACE; in_id < func.place_db.size (); - ++in_id) + for (PlaceId in_id = FIRST_VARIABLE_PLACE; + in_id.value < func.place_db.size (); ++in_id.value) { const Place &place = func.place_db[in_id]; if (place.kind == Place::VARIABLE || place.kind == Place::TEMPORARY) - place_map[in_id] = next_out_id++; + { + place_map[in_id.value] = next_out_id; + ++next_out_id.value; + } + else - place_map[in_id] = INVALID_PLACE; + place_map[in_id.value] = INVALID_PLACE; } } void -simplify_cfg (Function &func, std::vector<BasicBlockId> &bb_fold_map) +simplify_cfg (Function &func, IndexVec<BasicBlockId, BasicBlockId> &bb_fold_map) { // The BIR builder can generate many useless basic blocks, which contain only // a goto. @@ -78,7 +82,7 @@ simplify_cfg (Function &func, std::vector<BasicBlockId> &bb_fold_map) { stabilized = true; // BB0 cannot be folded as it is an entry block. - for (size_t i = 1; i < func.basic_blocks.size (); ++i) + for (BasicBlockId i = {1}; i.value < func.basic_blocks.size (); ++i.value) { const BasicBlock &bb = func.basic_blocks[bb_fold_map[i]]; if (bb.statements.empty () && bb.is_goto_terminated ()) @@ -93,7 +97,11 @@ simplify_cfg (Function &func, std::vector<BasicBlockId> &bb_fold_map) rust_inform (UNKNOWN_LOCATION, "Continuing with an unfolded CFG."); // Reverting the fold map to the original state. - std::iota (bb_fold_map.begin (), bb_fold_map.end (), 0); + for (BasicBlockId i = ENTRY_BASIC_BLOCK; + i.value < bb_fold_map.size (); ++i.value) + { + bb_fold_map[i] = i; + } stabilized = true; break; } @@ -108,9 +116,15 @@ void Dump::go (bool enable_simplify_cfg) { // To avoid mutation of the BIR, we use indirection through bb_fold_map. - std::iota (bb_fold_map.begin (), bb_fold_map.end (), 0); - - std::iota (place_map.begin (), place_map.end (), 0); + for (BasicBlockId i = ENTRY_BASIC_BLOCK; i.value < bb_fold_map.size (); + ++i.value) + { + bb_fold_map[i] = i; + } + for (PlaceId i = INVALID_PLACE; i.value < place_map.size (); ++i.value) + { + place_map[i] = i; + } if (enable_simplify_cfg) simplify_cfg (func, bb_fold_map); @@ -119,18 +133,18 @@ Dump::go (bool enable_simplify_cfg) stream << "fn " << name << "("; print_comma_separated (stream, func.arguments, [this] (PlaceId place_id) { - stream << "_" << place_map[place_id] << ": " + stream << "_" << place_map[place_id].value << ": " << get_tyty_name (func.place_db[place_id].tyty); }); stream << ") -> " << get_tyty_name (func.place_db[RETURN_VALUE_PLACE].tyty); stream << " {\n"; // Print locals declaration. - visit_scope (0); + visit_scope (ROOT_SCOPE); // Print BBs. - for (statement_bb = 0; statement_bb < func.basic_blocks.size (); - ++statement_bb) + for (statement_bb = ENTRY_BASIC_BLOCK; + statement_bb.value < func.basic_blocks.size (); ++statement_bb.value) { if (bb_fold_map[statement_bb] != statement_bb) continue; // This BB was folded. @@ -143,7 +157,8 @@ Dump::go (bool enable_simplify_cfg) BasicBlock &bb = func.basic_blocks[statement_bb]; stream << "\n"; - stream << indentation << "bb" << bb_fold_map[statement_bb] << ": {\n"; + stream << indentation << "bb" << bb_fold_map[statement_bb].value + << ": {\n"; size_t i = 0; for (auto &stmt : bb.statements) { @@ -153,7 +168,8 @@ Dump::go (bool enable_simplify_cfg) } if (!bb_terminated) stream << indentation << indentation << "goto -> bb" - << bb_fold_map[bb.successors.at (0)] << ";\t\t" << i++ << "\n"; + << bb_fold_map[bb.successors.at (0)].value << ";\t\t" << i++ + << "\n"; stream << indentation << "}\n"; } @@ -178,7 +194,7 @@ Dump::visit (const Statement &stmt) stream << ") -> ["; print_comma_separated (stream, func.basic_blocks[statement_bb].successors, [this] (BasicBlockId succ) { - stream << "bb" << bb_fold_map[succ]; + stream << "bb" << bb_fold_map[succ].value; }); stream << "]"; bb_terminated = true; @@ -188,8 +204,9 @@ Dump::visit (const Statement &stmt) bb_terminated = true; break; case Statement::Kind::GOTO: - stream << "goto -> bb" - << bb_fold_map[func.basic_blocks[statement_bb].successors.at (0)]; + stream + << "goto -> bb" + << bb_fold_map[func.basic_blocks[statement_bb].successors.at (0)].value; bb_terminated = true; break; case Statement::Kind::STORAGE_DEAD: @@ -228,7 +245,7 @@ Dump::visit_place (PlaceId place_id) { case Place::TEMPORARY: case Place::VARIABLE: - stream << "_" << place_map[place_id]; + stream << "_" << place_map[place_id].value; break; case Place::DEREF: stream << "("; @@ -272,7 +289,7 @@ Dump::visit (const BorrowExpr &expr) { stream << "&" << "'?" << expr.get_origin () << " "; - if (func.place_db.get_loans ()[expr.get_loan ()].mutability + if (func.place_db.get_loan (expr.get_loan_id ()).mutability == Mutability::Mut) stream << "mut "; visit_place (expr.get_place ()); @@ -311,7 +328,7 @@ Dump::visit (const CallExpr &expr) stream << ") -> ["; print_comma_separated (stream, func.basic_blocks[statement_bb].successors, [this] (BasicBlockId succ) { - stream << "bb" << bb_fold_map[succ]; + stream << "bb" << bb_fold_map[succ].value; }); stream << "]"; bb_terminated = true; @@ -359,13 +376,13 @@ Dump::visit_scope (ScopeId id, size_t depth) if (scope.locals.empty () && scope.children.empty ()) return; - if (id > 1) - indent (depth) << "scope " << id - 1 << " {\n"; + if (id.value > 1) + indent (depth) << "scope " << id.value - 1 << " {\n"; for (auto &local : scope.locals) { indent (depth + 1) << "let _"; - stream << place_map[local] << ": " + stream << place_map[local].value << ": " << get_tyty_name (func.place_db[local].tyty); stream << ";\t"; @@ -373,14 +390,14 @@ Dump::visit_scope (ScopeId id, size_t depth) print_comma_separated (stream, func.place_db[local].regions.get_regions (), [this] (FreeRegion region_id) { - stream << "'?" << region_id; + stream << "'?" << region_id.value; }); stream << "]\n"; } for (auto &child : scope.children) - visit_scope (child, (id >= 1) ? depth + 1 : depth); + visit_scope (child, (id.value >= 1) ? depth + 1 : depth); - if (id > 1) + if (id.value > 1) indent (depth) << "}\n"; } diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-dump.h b/gcc/rust/checks/errors/borrowck/rust-bir-dump.h index e88681f..1bf3f2d 100644 --- a/gcc/rust/checks/errors/borrowck/rust-bir-dump.h +++ b/gcc/rust/checks/errors/borrowck/rust-bir-dump.h @@ -34,8 +34,8 @@ class Dump : public Visitor Function &func; const std::string &name; - std::vector<BasicBlockId> bb_fold_map; - std::vector<PlaceId> place_map; + IndexVec<BasicBlockId, BasicBlockId> bb_fold_map; + IndexVec<PlaceId, PlaceId> place_map; PlaceId statement_place = INVALID_PLACE; BasicBlockId statement_bb = INVALID_BB; diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-fact-collector.h b/gcc/rust/checks/errors/borrowck/rust-bir-fact-collector.h index 9999bdd..1332ecf 100644 --- a/gcc/rust/checks/errors/borrowck/rust-bir-fact-collector.h +++ b/gcc/rust/checks/errors/borrowck/rust-bir-fact-collector.h @@ -40,14 +40,14 @@ class FactCollector : public Visitor // Read-only context. const PlaceDB &place_db; - const std::vector<BasicBlock> &basic_blocks; + const BasicBlocks &basic_blocks; const PlaceId first_local; const location_t location; Resolver::TypeCheckContext &tyctx; // Collector state. - BasicBlockId current_bb = 0; + BasicBlockId current_bb = ENTRY_BASIC_BLOCK; uint32_t current_stmt = 0; PlaceId lhs = INVALID_PLACE; @@ -65,11 +65,11 @@ class FactCollector : public Visitor FreeRegions make_fresh_regions (size_t size) { - std::vector<FreeRegion> free_regions; + FreeRegions free_regions; for (size_t i = 0; i < size; i++) free_regions.push_back (region_binder.get_next_free_region ()); - return FreeRegions (std::move (free_regions)); + return free_regions; } public: @@ -88,8 +88,9 @@ public: protected: // Constructor and destructor. explicit FactCollector (Function &func) : place_db (func.place_db), basic_blocks (func.basic_blocks), - first_local (func.arguments.empty () ? FIRST_VARIABLE_PLACE - : *func.arguments.rbegin () + 1), + first_local (func.arguments.empty () + ? FIRST_VARIABLE_PLACE + : PlaceId{func.arguments.rbegin ()->value + 1}), location (func.location), tyctx (*Resolver::TypeCheckContext::get ()), next_fresh_region (place_db.peek_next_free_region ()) {} @@ -106,19 +107,21 @@ protected: // Main collection entry points (for different categories). for (auto ®ion : universal_regions) { - facts.universal_region.emplace_back (region); - facts.placeholder.emplace_back (region, next_loan++); - facts.known_placeholder_subset.emplace_back (0, region); + facts.universal_region.emplace_back (region.value); + facts.placeholder.emplace_back (region.value, next_loan++); + facts.known_placeholder_subset.emplace_back (0, region.value); } // Copy already collected subset facts, that are universally valid. for (auto &bound : universal_region_bounds) - facts.known_placeholder_subset.emplace_back (bound.first, bound.second); + facts.known_placeholder_subset.emplace_back (bound.first.value, + bound.second.value); } void visit_places (const std::vector<PlaceId> &args) { - for (PlaceId place_id = 0; place_id < place_db.size (); ++place_id) + for (PlaceId place_id = INVALID_PLACE; place_id.value < place_db.size (); + ++place_id.value) { auto &place = place_db[place_id]; @@ -126,24 +129,28 @@ protected: // Main collection entry points (for different categories). { case Place::VARIABLE: case Place::TEMPORARY: - facts.path_is_var.emplace_back (place_id, place_id); + facts.path_is_var.emplace_back (place_id.value, place_id.value); for (auto ®ion : place.regions) - facts.use_of_var_derefs_origin.emplace_back (place_id, region); + facts.use_of_var_derefs_origin.emplace_back (place_id.value, + region.value); // TODO: drop_of_var_derefs_origin break; case Place::FIELD: sanizite_field (place_id); - facts.child_path.emplace_back (place_id, place.path.parent); + facts.child_path.emplace_back (place_id.value, + place.path.parent.value); break; case Place::INDEX: push_subset_all (place.tyty, place.regions, place_db[place.path.parent].regions); - facts.child_path.emplace_back (place_id, place.path.parent); + facts.child_path.emplace_back (place_id.value, + place.path.parent.value); break; case Place::DEREF: sanitize_deref (place_id); - facts.child_path.emplace_back (place_id, place.path.parent); + facts.child_path.emplace_back (place_id.value, + place.path.parent.value); break; case Place::CONSTANT: case Place::INVALID: @@ -151,15 +158,18 @@ protected: // Main collection entry points (for different categories). } } - for (PlaceId arg = FIRST_VARIABLE_PLACE + 1; arg < first_local; ++arg) + for (PlaceId arg = PlaceId{FIRST_VARIABLE_PLACE.value + 1}; + arg < first_local; ++arg.value) facts.path_assigned_at_base.emplace_back ( - arg, get_point (0, 0, PointPosition::START)); + arg.value, get_point (ENTRY_BASIC_BLOCK, 0, PointPosition::START)); - for (PlaceId place = first_local; place < place_db.size (); ++place) + for (PlaceId place = first_local; place.value < place_db.size (); + ++place.value) { if (place_db[place].is_var ()) facts.path_moved_at_base.emplace_back ( - place, get_point (0, 0, PointPosition::START)); + place.value, + get_point (ENTRY_BASIC_BLOCK, 0, PointPosition::START)); } } @@ -170,11 +180,12 @@ protected: // Main collection entry points (for different categories). rust_debug ("\tSanitize deref of %s", base.tyty->as_string ().c_str ()); - std::vector<Polonius::Origin> regions; - regions.insert (regions.end (), base.regions.begin () + 1, - base.regions.end ()); - FreeRegions r (std::move (regions)); - push_subset_all (place.tyty, r, place.regions); + FreeRegions regions; + for (auto it = base.regions.begin () + 1; it != base.regions.end (); ++it) + { + regions.push_back (*it); + } + push_subset_all (place.tyty, regions, place.regions); } void sanizite_field (PlaceId place_id) { @@ -191,15 +202,15 @@ protected: // Main collection entry points (for different categories). .query_field_regions (base.tyty->as<TyTy::ADTType> (), 0, place.variable_or_field_index, base.regions); // FIXME - FreeRegions f (std::move (r)); - push_subset_all (place.tyty, f, place.regions); + push_subset_all (place.tyty, r, place.regions); } void visit_statemensts () { rust_debug ("visit_statemensts"); - for (current_bb = 0; current_bb < basic_blocks.size (); ++current_bb) + for (current_bb = ENTRY_BASIC_BLOCK; + current_bb.value < basic_blocks.size (); ++current_bb.value) { auto &bb = basic_blocks[current_bb]; for (current_stmt = 0; current_stmt < bb.statements.size (); @@ -213,7 +224,7 @@ protected: // Main collection entry points (for different categories). visit (bb.statements[current_stmt]); } } - current_bb = 0; + current_bb = ENTRY_BASIC_BLOCK; current_stmt = 0; } @@ -242,9 +253,9 @@ protected: // Main collection entry points (for different categories). break; } case Statement::Kind::STORAGE_DEAD: { - facts.path_moved_at_base.emplace_back (stmt.get_place (), + facts.path_moved_at_base.emplace_back (stmt.get_place ().value, get_current_point_mid ()); - facts.var_defined_at.emplace_back (stmt.get_place (), + facts.var_defined_at.emplace_back (stmt.get_place ().value, get_current_point_mid ()); break; } @@ -293,9 +304,10 @@ protected: // Main collection entry points (for different categories). void visit (const BorrowExpr &expr) override { - rust_debug ("\t_%u = BorrowExpr(_%u)", lhs - 1, expr.get_place () - 1); + rust_debug ("\t_%u = BorrowExpr(_%u)", lhs.value - 1, + expr.get_place ().value - 1); - auto loan = place_db.get_loans ()[expr.get_loan ()]; + auto loan = place_db.get_loan (expr.get_loan_id ()); auto &base_place = place_db[expr.get_place ()]; auto &ref_place = place_db[lhs]; @@ -314,24 +326,24 @@ protected: // Main collection entry points (for different categories). ->is_mutable ()) rust_error_at (location, "Cannot reborrow immutable borrow as mutable"); - issue_loan (expr.get_origin (), expr.get_loan ()); + issue_loan (expr.get_origin (), expr.get_loan_id ()); } - push_subset (main_loan_place.regions[0], expr.get_origin ()); + push_subset (main_loan_place.regions[0], {expr.get_origin ()}); } else { - issue_loan (expr.get_origin (), expr.get_loan ()); + issue_loan (expr.get_origin (), expr.get_loan_id ()); } - auto loan_regions = base_place.regions.prepend (expr.get_origin ()); + auto loan_regions = base_place.regions.prepend ({expr.get_origin ()}); push_subset (ref_place.tyty, loan_regions, ref_place.regions); } void visit (const Assignment &expr) override { - rust_debug ("\t_%u = Assignment(_%u) at %u:%u", lhs - 1, - expr.get_rhs () - 1, current_bb, current_stmt); + rust_debug ("\t_%u = Assignment(_%u) at %u:%u", lhs.value - 1, + expr.get_rhs ().value - 1, current_bb.value, current_stmt); issue_read_move (expr.get_rhs ()); push_place_subset (lhs, expr.get_rhs ()); @@ -339,7 +351,8 @@ protected: // Main collection entry points (for different categories). void visit (const CallExpr &expr) override { - rust_debug ("\t_%u = CallExpr(_%u)", lhs - 1, expr.get_callable () - 1); + rust_debug ("\t_%u = CallExpr(_%u)", lhs.value - 1, + expr.get_callable ().value - 1); auto &return_place = place_db[lhs]; auto &callable_place = place_db[expr.get_callable ()]; @@ -387,7 +400,7 @@ protected: // Statement visitor helpers get_point (BasicBlockId bb, uint32_t stmt, PointPosition pos) { Polonius::Point point = 0; - point |= (bb << 16); + point |= (bb.value << 16); point |= (stmt << 1); point |= (static_cast<uint8_t> (pos) & 1); return point; @@ -434,14 +447,14 @@ protected: // Generic BIR operations. return; if (place_id != RETURN_VALUE_PLACE) - facts.path_accessed_at_base.emplace_back (place_id, + facts.path_accessed_at_base.emplace_back (place_id.value, get_current_point_mid ()); if (place.is_var ()) - facts.var_used_at.emplace_back (place_id, get_current_point_mid ()); + facts.var_used_at.emplace_back (place_id.value, get_current_point_mid ()); else if (place.is_path ()) { - facts.var_used_at.emplace_back (place_db.get_var (place_id), + facts.var_used_at.emplace_back (place_db.get_var (place_id).value, get_current_point_mid ()); } } @@ -468,11 +481,12 @@ protected: // Generic BIR operations. rust_assert (place.is_lvalue () || place.is_rvalue ()); if (place.is_var ()) - facts.var_defined_at.emplace_back (place_id, get_current_point_mid ()); + facts.var_defined_at.emplace_back (place_id.value, + get_current_point_mid ()); if (!is_init) { - facts.path_assigned_at_base.emplace_back (place_id, + facts.path_assigned_at_base.emplace_back (place_id.value, get_current_point_mid ()); check_write_for_conflict (place_id); kill_borrows_for_place (place_id); @@ -484,9 +498,11 @@ protected: // Generic BIR operations. if (!place_db[place_id].should_be_moved ()) return; - facts.path_moved_at_base.emplace_back ( - place_id, initial ? get_point (0, 0, PointPosition::START) - : get_current_point_mid ()); + facts.path_moved_at_base.emplace_back (place_id.value, + initial + ? get_point (ENTRY_BASIC_BLOCK, 0, + PointPosition::START) + : get_current_point_mid ()); check_move_behind_reference (place_id); @@ -499,24 +515,25 @@ protected: // Generic BIR operations. void issue_loan (Polonius::Origin origin, LoanId loan_id) { - facts.loan_issued_at.emplace_back (origin, loan_id, + facts.loan_issued_at.emplace_back (origin, loan_id.value, get_current_point_mid ()); - check_for_borrow_conficts (place_db.get_loans ()[loan_id].place, loan_id, - place_db.get_loans ()[loan_id].mutability); + check_for_borrow_conficts (place_db.get_loan (loan_id).place, loan_id, + place_db.get_loan (loan_id).mutability); } void issue_locals_dealloc () { - for (LoanId loan_id = 0; loan_id < place_db.get_loans ().size (); ++loan_id) + for (LoanId loan_id = {0}; loan_id.value < place_db.get_loans ().size (); + ++loan_id.value) { - auto &loan = place_db.get_loans ()[loan_id]; + auto &loan = place_db.get_loan (loan_id); auto loaned_var_id = place_db.get_var (loan.place); if (place_db[loaned_var_id].tyty->is<TyTy::ReferenceType> ()) continue; if (loaned_var_id >= first_local) facts.loan_invalidated_at.emplace_back (get_current_point_start (), - loan_id); + loan_id.value); } } @@ -534,20 +551,20 @@ protected: // Generic BIR operations. place_db.for_each_path_segment (place_id, [&] (PlaceId id) { for (auto loan : place_db[id].borrowed_by) { - if (place_db.get_loans ()[loan].mutability == Mutability::Mut) + if (place_db.get_loan (loan).mutability == Mutability::Mut) { facts.loan_invalidated_at.emplace_back ( - get_current_point_start (), loan); + get_current_point_start (), loan.value); } } }); place_db.for_each_path_from_root (place_id, [&] (PlaceId id) { for (auto loan : place_db[id].borrowed_by) { - if (place_db.get_loans ()[loan].mutability == Mutability::Mut) + if (place_db.get_loan (loan).mutability == Mutability::Mut) { facts.loan_invalidated_at.emplace_back ( - get_current_point_start (), loan); + get_current_point_start (), loan.value); } } }); @@ -558,12 +575,12 @@ protected: // Generic BIR operations. place_db.for_each_path_segment (place_id, [&] (PlaceId id) { for (auto loan : place_db[id].borrowed_by) facts.loan_invalidated_at.emplace_back (get_current_point_start (), - loan); + loan.value); }); place_db.for_each_path_from_root (place_id, [&] (PlaceId id) { for (auto loan : place_db[id].borrowed_by) facts.loan_invalidated_at.emplace_back (get_current_point_start (), - loan); + loan.value); }); } @@ -574,12 +591,11 @@ protected: // Generic BIR operations. for (auto other_loan : place_db[id].borrowed_by) { if (mutability == Mutability::Imm - && place_db.get_loans ()[other_loan].mutability - == Mutability::Imm) + && place_db.get_loan (other_loan).mutability == Mutability::Imm) continue; else facts.loan_invalidated_at.emplace_back (get_current_point_start (), - other_loan); + other_loan.value); } }); @@ -587,12 +603,11 @@ protected: // Generic BIR operations. for (auto other_loan : place_db[id].borrowed_by) { if (mutability == Mutability::Imm - && place_db.get_loans ()[other_loan].mutability - == Mutability::Imm) + && place_db.get_loan (other_loan).mutability == Mutability::Imm) continue; else facts.loan_invalidated_at.emplace_back (get_current_point_start (), - other_loan); + other_loan.value); } }); } @@ -614,26 +629,28 @@ protected: // Generic BIR operations. { // TODO: this is more complicated, see // compiler/rustc_borrowck/src/constraint_generation.rs:176 - facts.loan_killed_at.emplace_back (loan, get_current_point_mid ()); + facts.loan_killed_at.emplace_back (loan.value, + get_current_point_mid ()); } } protected: // Subset helpers. void push_subset (FreeRegion lhs, FreeRegion rhs) { - rust_debug ("\t\tpush_subset: '?%lu: '?%lu", (unsigned long) lhs, - (unsigned long) rhs); + rust_debug ("\t\tpush_subset: '?%lu: '?%lu", (unsigned long) lhs.value, + (unsigned long) rhs.value); - facts.subset_base.emplace_back (lhs, rhs, get_current_point_mid ()); + facts.subset_base.emplace_back (lhs.value, rhs.value, + get_current_point_mid ()); } void push_subset_all (FreeRegion lhs, FreeRegion rhs) { - rust_debug ("\t\tpush_subset_all: '?%lu: '?%lu", (unsigned long) lhs, - (unsigned long) rhs); + rust_debug ("\t\tpush_subset_all: '?%lu: '?%lu", (unsigned long) lhs.value, + (unsigned long) rhs.value); for (auto point : cfg_points_all) - facts.subset_base.emplace_back (lhs, rhs, point); + facts.subset_base.emplace_back (lhs.value, rhs.value, point); } void push_subset (Variance variance, FreeRegion lhs, FreeRegion rhs) diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-free-region.h b/gcc/rust/checks/errors/borrowck/rust-bir-free-region.h index c09e888..cb459f8 100644 --- a/gcc/rust/checks/errors/borrowck/rust-bir-free-region.h +++ b/gcc/rust/checks/errors/borrowck/rust-bir-free-region.h @@ -24,7 +24,19 @@ namespace Rust { -using FreeRegion = size_t; +struct FreeRegion +{ + size_t value; + // some overloads for comparision + bool operator== (const FreeRegion &rhs) const { return value == rhs.value; } + bool operator!= (const FreeRegion &rhs) const { return !(operator== (rhs)); } + bool operator< (const FreeRegion &rhs) const { return value < rhs.value; } + bool operator> (const FreeRegion &rhs) const { return value > rhs.value; } + bool operator<= (const FreeRegion &rhs) const { return !(operator> (rhs)); } + bool operator>= (const FreeRegion &rhs) const { return !(operator< (rhs)); } +}; + +static constexpr FreeRegion STATIC_FREE_REGION = {0}; class FreeRegions { @@ -38,14 +50,6 @@ public: FreeRegion &operator[] (size_t i) { return regions.at (i); } const FreeRegion &operator[] (size_t i) const { return regions.at (i); } const std::vector<FreeRegion> &get_regions () const { return regions; } - void set_from (std::vector<Rust::Polonius::Origin> &®ions) - { - this->regions.clear (); - for (auto ®ion : regions) - { - this->regions.push_back ({region}); - } - } WARN_UNUSED_RESULT FreeRegions prepend (FreeRegion region) const { @@ -54,6 +58,9 @@ public: return FreeRegions (std::move (new_regions)); } + void push_back (FreeRegion region) { regions.push_back (region); } + + FreeRegions () {} FreeRegions (std::vector<FreeRegion> &®ions) : regions (regions) {} WARN_UNUSED_RESULT std::string to_string () const @@ -61,7 +68,7 @@ public: std::stringstream result; for (auto ®ion : regions) { - result << region; + result << region.value; result << ", "; } // Remove the last ", " from the string. @@ -83,19 +90,20 @@ public: WARN_UNUSED_RESULT FreeRegion get_next_free_region () const { - return next_free_region++; + ++next_free_region.value; + return {next_free_region.value - 1}; } FreeRegions bind_regions (std::vector<TyTy::Region> regions, FreeRegions parent_free_regions) { - std::vector<FreeRegion> free_regions; + FreeRegions free_regions; for (auto ®ion : regions) { if (region.is_early_bound ()) free_regions.push_back (parent_free_regions[region.get_index ()]); else if (region.is_static ()) - free_regions.push_back (0); + free_regions.push_back (STATIC_FREE_REGION); else if (region.is_anonymous ()) free_regions.push_back (get_next_free_region ()); else if (region.is_named ()) @@ -106,9 +114,7 @@ public: rust_unreachable (); } } - // This is necesarry because of clash of current gcc and gcc4.8. - FreeRegions free_regions_final{std::move (free_regions)}; - return free_regions_final; + return free_regions; } }; diff --git a/gcc/rust/checks/errors/borrowck/rust-bir-place.h b/gcc/rust/checks/errors/borrowck/rust-bir-place.h index 8c38e8e..a1621b7 100644 --- a/gcc/rust/checks/errors/borrowck/rust-bir-place.h +++ b/gcc/rust/checks/errors/borrowck/rust-bir-place.h @@ -32,14 +32,36 @@ namespace Rust { namespace BIR { /** A unique identifier for a place in the BIR. */ -using PlaceId = uint32_t; +struct PlaceId +{ + uint32_t value; + // some overloads for comparision + bool operator== (const PlaceId &rhs) const { return value == rhs.value; } + bool operator!= (const PlaceId &rhs) const { return !(operator== (rhs)); } + bool operator< (const PlaceId &rhs) const { return value < rhs.value; } + bool operator> (const PlaceId &rhs) const { return value > rhs.value; } + bool operator<= (const PlaceId &rhs) const { return !(operator> (rhs)); } + bool operator>= (const PlaceId &rhs) const { return !(operator< (rhs)); } +}; -static constexpr PlaceId INVALID_PLACE = 0; -static constexpr PlaceId RETURN_VALUE_PLACE = 1; +static constexpr PlaceId INVALID_PLACE = {0}; +static constexpr PlaceId RETURN_VALUE_PLACE = {1}; static constexpr PlaceId FIRST_VARIABLE_PLACE = RETURN_VALUE_PLACE; using Variance = TyTy::VarianceAnalysis::Variance; -using LoanId = uint32_t; + +/** A unique identifier for a loan in the BIR. */ +struct LoanId +{ + uint32_t value; + // some overloads for comparision + bool operator== (const LoanId &rhs) const { return value == rhs.value; } + bool operator!= (const LoanId &rhs) const { return !(operator== (rhs)); } + bool operator< (const LoanId &rhs) const { return value < rhs.value; } + bool operator> (const LoanId &rhs) const { return value > rhs.value; } + bool operator<= (const LoanId &rhs) const { return !(operator> (rhs)); } + bool operator>= (const LoanId &rhs) const { return !(operator< (rhs)); } +}; /** * Representation of lvalues and constants in BIR. @@ -143,13 +165,25 @@ public: } }; -using ScopeId = uint32_t; +struct ScopeId +{ + uint32_t value; + ScopeId next_scope_id () const { return {value + 1}; } + // some overloads for comparision + bool operator== (const ScopeId &rhs) const { return value == rhs.value; } + bool operator!= (const ScopeId &rhs) const { return !(operator== (rhs)); } + bool operator< (const ScopeId &rhs) const { return value < rhs.value; } + bool operator> (const ScopeId &rhs) const { return value > rhs.value; } + bool operator<= (const ScopeId &rhs) const { return !(operator> (rhs)); } + bool operator>= (const ScopeId &rhs) const { return !(operator< (rhs)); } +}; -static constexpr ScopeId INVALID_SCOPE = std::numeric_limits<ScopeId>::max (); +static constexpr ScopeId INVALID_SCOPE + = {std::numeric_limits<uint32_t>::max ()}; /** Arguments and return value are in the root scope. */ -static constexpr ScopeId ROOT_SCOPE = 0; +static constexpr ScopeId ROOT_SCOPE = {0}; /** Top-level local variables are in the top-level scope. */ -static constexpr ScopeId TOP_LEVEL_SCOPE = 1; +static constexpr ScopeId TOP_LEVEL_SCOPE = {1}; struct Scope { @@ -162,21 +196,51 @@ struct Loan { Mutability mutability; PlaceId place; + location_t location; +}; + +// I is the index type, T is the contained type +template <typename I, typename T> class IndexVec +{ + std::vector<T> internal_vector; + +public: + IndexVec () = default; + IndexVec (size_t size) { internal_vector.reserve (size); } + + T &at (I pid) { return internal_vector[pid.value]; } + const T &at (I pid) const { return internal_vector[pid.value]; } + T &operator[] (I pid) { return internal_vector[pid.value]; } + const T &operator[] (I pid) const { return internal_vector[pid.value]; } + + void push_back (T &¶m) { internal_vector.push_back (std::move (param)); } + template <typename... Args> void emplace_back (Args &&... args) + { + internal_vector.emplace_back (std::forward<Args> (args)...); + } + + size_t size () const { return internal_vector.size (); } + + std::vector<T> &get_vector () { return internal_vector; } }; +using Scopes = IndexVec<ScopeId, Scope>; +using Loans = IndexVec<LoanId, Loan>; +using Places = IndexVec<PlaceId, Place>; + /** Allocated places and keeps track of paths. */ class PlaceDB { private: // Possible optimizations: separate variables to speedup lookup. - std::vector<Place> places; + Places places; std::unordered_map<TyTy::BaseType *, PlaceId> constants_lookup; - std::vector<Scope> scopes; - ScopeId current_scope = 0; + Scopes scopes; + ScopeId current_scope = ROOT_SCOPE; - std::vector<Loan> loans; + Loans loans; - Polonius::Origin next_free_region = 1; + FreeRegion next_free_region = {1}; public: PlaceDB () @@ -190,22 +254,24 @@ public: Place &operator[] (PlaceId id) { return places.at (id); } const Place &operator[] (PlaceId id) const { return places.at (id); } - decltype (places)::const_iterator begin () const { return places.begin (); } - decltype (places)::const_iterator end () const { return places.end (); } - size_t size () const { return places.size (); } - const std::vector<Loan> &get_loans () const { return loans; } + const Loans &get_loans () const { return loans; } + const Loan &get_loan (LoanId loan_id) const { return loans.at (loan_id); } ScopeId get_current_scope_id () const { return current_scope; } - const std::vector<Scope> &get_scopes () const { return scopes; } + const Scopes &get_scopes () const { return scopes; } const Scope &get_current_scope () const { return scopes[current_scope]; } const Scope &get_scope (ScopeId id) const { return scopes[id]; } - FreeRegion get_next_free_region () { return next_free_region++; } + FreeRegion get_next_free_region () + { + ++next_free_region.value; + return {next_free_region.value - 1}; + } FreeRegion peek_next_free_region () const { return next_free_region; } @@ -213,7 +279,7 @@ public: ScopeId push_new_scope () { - ScopeId new_scope = scopes.size (); + ScopeId new_scope = {scopes.size ()}; scopes.emplace_back (); scopes[new_scope].parent = current_scope; scopes[current_scope].children.push_back (new_scope); @@ -227,12 +293,12 @@ public: return current_scope; } - PlaceId add_place (Place &&place, PlaceId last_sibling = 0) + PlaceId add_place (Place &&place, PlaceId last_sibling = INVALID_PLACE) { places.emplace_back (std::forward<Place &&> (place)); - PlaceId new_place = places.size () - 1; + PlaceId new_place = {places.size () - 1}; Place &new_place_ref = places[new_place]; // Intentional shadowing. - if (last_sibling == 0) + if (last_sibling == INVALID_PLACE) places[new_place_ref.path.parent].path.first_child = new_place; else places[last_sibling].path.next_sibling = new_place; @@ -244,29 +310,33 @@ public: auto variances = Resolver::TypeCheckContext::get () ->get_variance_analysis_ctx () .query_type_variances (new_place_ref.tyty); - std::vector<Polonius::Origin> regions; - for (size_t i = 0; i < variances.size (); i++) - regions.push_back (next_free_region++); + FreeRegions regions; + for (size_t i = 0; i < variances.size (); ++i) + { + regions.push_back (next_free_region); + ++next_free_region.value; + } - new_place_ref.regions.set_from (std::move (regions)); + new_place_ref.regions = regions; return new_place; } PlaceId add_variable (NodeId id, TyTy::BaseType *tyty) { - return add_place ({Place::VARIABLE, id, {}, is_type_copy (tyty), tyty}, 0); + return add_place ({Place::VARIABLE, id, {}, is_type_copy (tyty), tyty}, + INVALID_PLACE); } WARN_UNUSED_RESULT PlaceId lookup_or_add_path (Place::Kind kind, TyTy::BaseType *tyty, PlaceId parent, size_t id = 0) { - PlaceId current = 0; - if (parent < places.size ()) + PlaceId current = INVALID_PLACE; + if (parent.value < places.size ()) { current = places[parent].path.first_child; - while (current != 0) + while (current != INVALID_PLACE) { if (places[current].kind == kind && places[current].variable_or_field_index == id) @@ -277,14 +347,16 @@ public: current = places[current].path.next_sibling; } } - return add_place ({kind, (uint32_t) id, Place::Path{parent, 0, 0}, + return add_place ({kind, (uint32_t) id, + Place::Path{parent, INVALID_PLACE, INVALID_PLACE}, is_type_copy (tyty), tyty}, current); } PlaceId add_temporary (TyTy::BaseType *tyty) { - return add_place ({Place::TEMPORARY, 0, {}, is_type_copy (tyty), tyty}, 0); + return add_place ({Place::TEMPORARY, 0, {}, is_type_copy (tyty), tyty}, + INVALID_PLACE); } PlaceId get_constant (TyTy::BaseType *tyty) @@ -299,22 +371,22 @@ public: { PlaceId current = FIRST_VARIABLE_PLACE; - while (current != places.size ()) + while (current.value != places.size ()) { if (places[current].kind == Place::VARIABLE && places[current].variable_or_field_index == id) return current; - current++; + ++current.value; } return INVALID_PLACE; } LoanId add_loan (Loan &&loan) { - LoanId id = loans.size (); + LoanId id = {loans.size ()}; loans.push_back (std::forward<Loan &&> (loan)); - PlaceId borrowed_place = loans.rbegin ()->place; - places[loans.rbegin ()->place].borrowed_by.push_back (id); + PlaceId borrowed_place = loans.get_vector ().rbegin ()->place; + places[loans.get_vector ().rbegin ()->place].borrowed_by.push_back (id); if (places[borrowed_place].kind == Place::DEREF) { places[places[borrowed_place].path.parent].borrowed_by.push_back (id); @@ -337,7 +409,7 @@ public: void set_next_free_region (Polonius::Origin next_free_region) { - this->next_free_region = next_free_region; + this->next_free_region.value = next_free_region; } PlaceId lookup_or_add_variable (NodeId id, TyTy::BaseType *tyty) @@ -347,7 +419,7 @@ public: return lookup; add_place ({Place::VARIABLE, id, {}, is_type_copy (tyty), tyty}); - return places.size () - 1; + return {places.size () - 1}; }; template <typename FN> void for_each_path_from_root (PlaceId var, FN fn) const diff --git a/gcc/rust/checks/errors/borrowck/rust-bir.h b/gcc/rust/checks/errors/borrowck/rust-bir.h index ed190a0..e90e508 100644 --- a/gcc/rust/checks/errors/borrowck/rust-bir.h +++ b/gcc/rust/checks/errors/borrowck/rust-bir.h @@ -30,11 +30,11 @@ namespace Rust { namespace BIR { struct BasicBlock; +struct BasicBlockId; +using BasicBlocks = IndexVec<BasicBlockId, BasicBlock>; class Statement; class AbstractExpr; -using LoanId = uint32_t; - /** * Top-level entity of the Borrow-checker IR (BIR). * It represents a single function (method, closure, etc.), which is the @@ -44,9 +44,10 @@ struct Function { PlaceDB place_db; std::vector<PlaceId> arguments; - std::vector<BasicBlock> basic_blocks; + BasicBlocks basic_blocks; FreeRegions universal_regions; std::vector<std::pair<FreeRegion, FreeRegion>> universal_region_bounds; + std::unordered_map<Polonius::Origin, HIR::LifetimeParam *> region_hir_map; location_t location; }; @@ -77,19 +78,50 @@ private: // otherwise: <unused> std::unique_ptr<AbstractExpr> expr; TyTy::BaseType *type; + // stores location of the actual expression from source code + // currently only available when kind is ASSIGNMENT | RETURN + // FIXME: Add location for other statement kinds + location_t location; public: - Statement (PlaceId lhs, AbstractExpr *rhs) - : kind (Kind::ASSIGNMENT), place (lhs), expr (rhs) - {} - - explicit Statement (Kind kind, PlaceId place = INVALID_PLACE, - AbstractExpr *expr = nullptr) - : kind (kind), place (place), expr (expr) - {} + static Statement make_assignment (PlaceId place, AbstractExpr *rhs, + location_t location) + { + return Statement (Kind::ASSIGNMENT, place, rhs, nullptr, location); + } + static Statement make_switch (PlaceId place) + { + return Statement (Kind::SWITCH, place); + } + static Statement make_return (location_t location) + { + return Statement (Kind::RETURN, INVALID_PLACE, nullptr, nullptr, location); + } + static Statement make_goto () { return Statement (Kind::GOTO); } + static Statement make_storage_dead (PlaceId place) + { + return Statement (Kind::STORAGE_DEAD, place); + } + static Statement make_storage_live (PlaceId place) + { + return Statement (Kind::STORAGE_LIVE, place); + } + static Statement make_user_type_ascription (PlaceId place, + TyTy::BaseType *type) + { + return Statement (Kind::USER_TYPE_ASCRIPTION, place, nullptr, type); + } + static Statement make_fake_read (PlaceId place) + { + return Statement (Kind::FAKE_READ, place); + } - explicit Statement (Kind kind, PlaceId place, TyTy::BaseType *type) - : kind (kind), place (place), type (type) +private: + // compelete constructor, used by make_* functions + Statement (Kind kind, PlaceId place = INVALID_PLACE, + AbstractExpr *rhs = nullptr, TyTy::BaseType *type = nullptr, + location_t location = UNKNOWN_LOCATION) + : kind (kind), place (place), expr (rhs), type (type), location (location) {} public: @@ -97,13 +129,28 @@ public: WARN_UNUSED_RESULT PlaceId get_place () const { return place; } WARN_UNUSED_RESULT AbstractExpr &get_expr () const { return *expr; } WARN_UNUSED_RESULT TyTy::BaseType *get_type () const { return type; } + WARN_UNUSED_RESULT location_t get_location () const { return location; } }; /** Unique identifier for a basic block in the BIR. */ -using BasicBlockId = uint32_t; +struct BasicBlockId +{ + uint32_t value; + // some overloads for comparision + bool operator== (const BasicBlockId &rhs) const { return value == rhs.value; } + bool operator!= (const BasicBlockId &rhs) const + { + return !(operator== (rhs)); + } + bool operator< (const BasicBlockId &rhs) const { return value < rhs.value; } + bool operator> (const BasicBlockId &rhs) const { return value > rhs.value; } + bool operator<= (const BasicBlockId &rhs) const { return !(operator> (rhs)); } + bool operator>= (const BasicBlockId &rhs) const { return !(operator< (rhs)); } +}; static constexpr BasicBlockId INVALID_BB - = std::numeric_limits<BasicBlockId>::max (); + = {std::numeric_limits<uint32_t>::max ()}; +static constexpr BasicBlockId ENTRY_BASIC_BLOCK = {0}; struct BasicBlock { @@ -192,7 +239,7 @@ public: loan (loan_id), origin (lifetime) {} WARN_UNUSED_RESULT PlaceId get_place () const { return place; } - WARN_UNUSED_RESULT LoanId get_loan () const { return loan; } + WARN_UNUSED_RESULT LoanId get_loan_id () const { return loan; } WARN_UNUSED_RESULT Polonius::Origin get_origin () const { return origin; } }; diff --git a/gcc/rust/checks/errors/borrowck/rust-borrow-checker-diagnostics.cc b/gcc/rust/checks/errors/borrowck/rust-borrow-checker-diagnostics.cc index a8eaa80..6c67706 100644 --- a/gcc/rust/checks/errors/borrowck/rust-borrow-checker-diagnostics.cc +++ b/gcc/rust/checks/errors/borrowck/rust-borrow-checker-diagnostics.cc @@ -17,6 +17,8 @@ // <http://www.gnu.org/licenses/>. #include "rust-borrow-checker-diagnostics.h" +#include "polonius/rust-polonius-ffi.h" +#include "rust-diagnostics.h" namespace Rust { namespace BIR { @@ -32,35 +34,136 @@ BorrowCheckerDiagnostics::report_errors () void BorrowCheckerDiagnostics::report_move_errors () { - if (!move_errors.empty ()) + for (const auto &pair : move_errors) { - rust_error_at (hir_function->get_locus (), - "Found move errors in function %s", - hir_function->get_function_name ().as_string ().c_str ()); + auto error_location = get_statement (pair.first).get_location (); + + // in future, we can use the assigned at location to hint the + // user to implement copy trait for the type + /* + for (auto it : facts.path_assigned_at_base) + { + if (pair.second[0] == it.first) + { + auto point_assigned_at = it.second; + auto assigned_at_location + = get_statement (point_assigned_at).get_location (); + } + } + */ + + std::vector<LabelLocationPair> labels{ + {"moved value used here", error_location}}; + // add labels to all the moves for the given path + for (auto it : facts.path_moved_at_base) + { + if (pair.second[0] == it.first) + { + auto point_moved_at = it.second; + // don't label the move location where the error occured + if (pair.first != point_moved_at) + { + auto move_at_location + = get_statement (point_moved_at).get_location (); + labels.push_back ({"value moved here", move_at_location}); + } + } + } + multi_label_error ("use of moved value", error_location, labels); } } void BorrowCheckerDiagnostics::report_loan_errors () { - if (!loan_errors.empty ()) + for (const auto &pair : loan_errors) { - rust_error_at (hir_function->get_locus (), - "Found loan errors in function %s", - hir_function->get_function_name ().as_string ().c_str ()); + auto error_location = get_statement (pair.first).get_location (); + for (const auto &loan : pair.second) + { + auto loan_struct = get_loan (loan); + multi_label_error ("use of borrowed value", error_location, + {{"borrow occurs here", loan_struct.location}, + {"borrowed value used here", error_location}}); + } } } void BorrowCheckerDiagnostics::report_subset_errors () { - if (!subset_errors.empty ()) + // remove duplicates in subset_errors + // + // Polonius may output subset errors for same 2 origins at multiple points + // so to avoid duplicating the errors, we can remove the elements in subset + // errors with same origin pair + std::vector<std::pair<size_t, std::pair<size_t, size_t>>> + deduplicated_subset_errors; + + for (auto pair : subset_errors) + { + auto it = std::find_if ( + deduplicated_subset_errors.begin (), deduplicated_subset_errors.end (), + [&pair] (std::pair<size_t, std::pair<size_t, size_t>> element) { + return element.second == pair.second; + }); + if (it == deduplicated_subset_errors.end ()) + { + deduplicated_subset_errors.push_back (pair); + } + } + for (const auto &error : deduplicated_subset_errors) + { + auto first_lifetime_location + = get_lifetime_param (error.second.first)->get_locus (); + auto second_lifetime_location + = get_lifetime_param (error.second.second)->get_locus (); + multi_label_error ( + "subset error, some lifetime constraints need to be added", + bir_function.location, + {{"lifetime defined here", first_lifetime_location}, + {"lifetime defined here", second_lifetime_location}, + {"subset error occurs in this function", bir_function.location}}); + } +} + +const BIR::Statement & +BorrowCheckerDiagnostics::get_statement (Polonius::Point point) +{ + auto statement_index = Polonius::FullPoint::extract_stmt (point); + auto bb_index = Polonius::FullPoint::extract_bb (point); + // assert that the extracted indexes are valid + rust_assert (bb_index < bir_function.basic_blocks.size ()); + rust_assert (statement_index + < bir_function.basic_blocks[{bb_index}].statements.size ()); + return bir_function.basic_blocks[{bb_index}].statements[statement_index]; +} + +const BIR::Loan & +BorrowCheckerDiagnostics::get_loan (Polonius::Loan loan) +{ + return bir_function.place_db.get_loans ()[{loan}]; +} + +const HIR::LifetimeParam * +BorrowCheckerDiagnostics::get_lifetime_param (Polonius::Origin origin) + +{ + return bir_function.region_hir_map.at (origin); +} + +void +BorrowCheckerDiagnostics::multi_label_error ( + const char *error_message, location_t error_location, + std::vector<LabelLocationPair> location_label_pairs) +{ + rich_location r{line_table, error_location}; + for (auto &label_location : location_label_pairs) { - rust_error_at (hir_function->get_locus (), - "Found subset errors in function %s. Some lifetime " - "constraints need to be added.", - hir_function->get_function_name ().as_string ().c_str ()); + r.add_range (label_location.location, SHOW_RANGE_WITHOUT_CARET, + &label_location.label); } + rust_error_at (r, "%s", error_message); } } // namespace BIR diff --git a/gcc/rust/checks/errors/borrowck/rust-borrow-checker-diagnostics.h b/gcc/rust/checks/errors/borrowck/rust-borrow-checker-diagnostics.h index 90d5ed8..9ab0591 100644 --- a/gcc/rust/checks/errors/borrowck/rust-borrow-checker-diagnostics.h +++ b/gcc/rust/checks/errors/borrowck/rust-borrow-checker-diagnostics.h @@ -22,6 +22,7 @@ #include "polonius/rust-polonius.h" #include "rust-bir.h" #include "rust-hir-item.h" +#include "text-range-label.h" namespace Rust { namespace BIR { @@ -62,6 +63,19 @@ private: void report_move_errors (); void report_loan_errors (); void report_subset_errors (); + + const BIR::Statement &get_statement (Polonius::Point point); + const BIR::Loan &get_loan (Polonius::Loan loan); + const HIR::LifetimeParam *get_lifetime_param (Polonius::Origin origin); + + struct LabelLocationPair + { + text_range_label label; + location_t location; + }; + static void + multi_label_error (const char *error_message, location_t error_location, + std::vector<LabelLocationPair> location_label_pairs); }; } // namespace BIR diff --git a/gcc/rust/checks/errors/borrowck/rust-function-collector.h b/gcc/rust/checks/errors/borrowck/rust-function-collector.h index 51109d7..5de503d 100644 --- a/gcc/rust/checks/errors/borrowck/rust-function-collector.h +++ b/gcc/rust/checks/errors/borrowck/rust-function-collector.h @@ -56,13 +56,13 @@ protected: void visit (HIR::Function &function) override { functions.push_back (&function); - function.get_definition ()->accept_vis (*this); + function.get_definition ().accept_vis (*this); } void visit (HIR::ClosureExpr &closure) override { closures.push_back (&closure); - closure.get_expr ()->accept_vis (*this); + closure.get_expr ().accept_vis (*this); } // TODO: recurse for nested closures and functions. @@ -119,8 +119,6 @@ public: void visit (HIR::WhileLetLoopExpr &expr) override {} void visit (HIR::IfExpr &expr) override {} void visit (HIR::IfExprConseqElse &expr) override {} - void visit (HIR::IfLetExpr &expr) override {} - void visit (HIR::IfLetExprConseqElse &expr) override {} void visit (HIR::MatchExpr &expr) override {} void visit (HIR::AwaitExpr &expr) override {} void visit (HIR::AsyncBlockExpr &expr) override {} diff --git a/gcc/rust/checks/errors/privacy/rust-privacy-reporter.cc b/gcc/rust/checks/errors/privacy/rust-privacy-reporter.cc index d16d6ed..dcc768f 100644 --- a/gcc/rust/checks/errors/privacy/rust-privacy-reporter.cc +++ b/gcc/rust/checks/errors/privacy/rust-privacy-reporter.cc @@ -22,6 +22,7 @@ #include "rust-hir-stmt.h" #include "rust-hir-item.h" #include "rust-attribute-values.h" +#include "rust-immutable-name-resolution-context.h" namespace Rust { namespace Privacy { @@ -93,6 +94,14 @@ static bool is_child_module (Analysis::Mappings &mappings, NodeId parent, NodeId possible_child) { + if (flag_name_resolution_2_0) + { + auto &nr_ctx + = Resolver2_0::ImmutableNameResolutionContext::get ().resolver (); + + return nr_ctx.values.is_module_descendant (parent, possible_child); + } + auto children = mappings.lookup_module_children (parent); if (!children) @@ -118,8 +127,16 @@ PrivacyReporter::check_for_privacy_violation (const NodeId &use_id, { NodeId ref_node_id = UNKNOWN_NODEID; + if (flag_name_resolution_2_0) + { + auto &nr_ctx + = Resolver2_0::ImmutableNameResolutionContext::get ().resolver (); + + if (auto id = nr_ctx.lookup (use_id)) + ref_node_id = *id; + } // FIXME: Don't assert here - we might be dealing with a type - if (!resolver.lookup_resolved_name (use_id, &ref_node_id)) + else if (!resolver.lookup_resolved_name (use_id, &ref_node_id)) resolver.lookup_resolved_type (use_id, &ref_node_id); // FIXME: Assert here. For now, we return since this causes issues when @@ -258,16 +275,13 @@ PrivacyReporter::check_base_type_privacy (Analysis::NodeMapping &node_mappings, } void -PrivacyReporter::check_type_privacy (const HIR::Type *type) +PrivacyReporter::check_type_privacy (const HIR::Type &type) { - rust_assert (type); - TyTy::BaseType *lookup = nullptr; - rust_assert ( - ty_ctx.lookup_type (type->get_mappings ().get_hirid (), &lookup)); + rust_assert (ty_ctx.lookup_type (type.get_mappings ().get_hirid (), &lookup)); - auto node_mappings = type->get_mappings (); - return check_base_type_privacy (node_mappings, lookup, type->get_locus ()); + auto node_mappings = type.get_mappings (); + return check_base_type_privacy (node_mappings, lookup, type.get_locus ()); } void @@ -284,6 +298,10 @@ PrivacyReporter::visit (HIR::TypePathSegmentFunction &) } void +PrivacyReporter::visit (HIR::InlineAsm &) +{} + +void PrivacyReporter::visit (HIR::TypePath &path) { check_for_privacy_violation (path.get_mappings ().get_nodeid (), @@ -313,100 +331,98 @@ PrivacyReporter::visit (HIR::LiteralExpr &) void PrivacyReporter::visit (HIR::BorrowExpr &expr) { - expr.get_expr ()->accept_vis (*this); + expr.get_expr ().accept_vis (*this); } void PrivacyReporter::visit (HIR::DereferenceExpr &expr) { - expr.get_expr ()->accept_vis (*this); + expr.get_expr ().accept_vis (*this); } void PrivacyReporter::visit (HIR::ErrorPropagationExpr &expr) { - expr.get_expr ()->accept_vis (*this); + expr.get_expr ().accept_vis (*this); } void PrivacyReporter::visit (HIR::NegationExpr &expr) { - expr.get_expr ()->accept_vis (*this); + expr.get_expr ().accept_vis (*this); } void PrivacyReporter::visit (HIR::ArithmeticOrLogicalExpr &expr) { - expr.get_lhs ()->accept_vis (*this); - expr.get_rhs ()->accept_vis (*this); + expr.get_lhs ().accept_vis (*this); + expr.get_rhs ().accept_vis (*this); } void PrivacyReporter::visit (HIR::ComparisonExpr &expr) { - expr.get_lhs ()->accept_vis (*this); - expr.get_rhs ()->accept_vis (*this); + expr.get_lhs ().accept_vis (*this); + expr.get_rhs ().accept_vis (*this); } void PrivacyReporter::visit (HIR::LazyBooleanExpr &expr) { - expr.get_lhs ()->accept_vis (*this); - expr.get_rhs ()->accept_vis (*this); + expr.get_lhs ().accept_vis (*this); + expr.get_rhs ().accept_vis (*this); } void PrivacyReporter::visit (HIR::TypeCastExpr &expr) { - expr.get_expr ()->accept_vis (*this); + expr.get_expr ().accept_vis (*this); } void PrivacyReporter::visit (HIR::AssignmentExpr &expr) { - expr.get_lhs ()->accept_vis (*this); - expr.get_rhs ()->accept_vis (*this); + expr.get_lhs ().accept_vis (*this); + expr.get_rhs ().accept_vis (*this); } void PrivacyReporter::visit (HIR::CompoundAssignmentExpr &expr) { - expr.get_lhs ()->accept_vis (*this); - expr.get_rhs ()->accept_vis (*this); + expr.get_lhs ().accept_vis (*this); + expr.get_rhs ().accept_vis (*this); } void PrivacyReporter::visit (HIR::GroupedExpr &expr) { - expr.get_expr_in_parens ()->accept_vis (*this); + expr.get_expr_in_parens ().accept_vis (*this); } void PrivacyReporter::visit (HIR::ArrayExpr &expr) { - HIR::ArrayElems &elements = *expr.get_internal_elements (); + HIR::ArrayElems &elements = expr.get_internal_elements (); switch (elements.get_array_expr_type ()) { case HIR::ArrayElems::ArrayExprType::VALUES: { - HIR::ArrayElemsValues &elems - = static_cast<HIR::ArrayElemsValues &> (elements); + auto &elems = static_cast<HIR::ArrayElemsValues &> (elements); for (auto &value : elems.get_values ()) value->accept_vis (*this); } return; case HIR::ArrayElems::ArrayExprType::COPIED: - HIR::ArrayElemsCopied &elems - = static_cast<HIR::ArrayElemsCopied &> (elements); - elems.get_elem_to_copy ()->accept_vis (*this); + auto &elems = static_cast<HIR::ArrayElemsCopied &> (elements); + elems.get_elem_to_copy ().accept_vis (*this); } } void PrivacyReporter::visit (HIR::ArrayIndexExpr &expr) { - expr.get_array_expr ()->accept_vis (*this); - expr.get_index_expr ()->accept_vis (*this); + expr.get_array_expr ().accept_vis (*this); + expr.get_index_expr ().accept_vis (*this); } void @@ -419,7 +435,7 @@ PrivacyReporter::visit (HIR::TupleExpr &expr) void PrivacyReporter::visit (HIR::TupleIndexExpr &expr) { - expr.get_tuple_expr ()->accept_vis (*this); + expr.get_tuple_expr ().accept_vis (*this); } void @@ -435,13 +451,13 @@ PrivacyReporter::visit (HIR::StructExprFieldIdentifier &) void PrivacyReporter::visit (HIR::StructExprFieldIdentifierValue &field) { - field.get_value ()->accept_vis (*this); + field.get_value ().accept_vis (*this); } void PrivacyReporter::visit (HIR::StructExprFieldIndexValue &field) { - field.get_value ()->accept_vis (*this); + field.get_value ().accept_vis (*this); } void @@ -454,7 +470,7 @@ PrivacyReporter::visit (HIR::StructExprStructFields &expr) void PrivacyReporter::visit (HIR::CallExpr &expr) { - expr.get_fnexpr ()->accept_vis (*this); + expr.get_fnexpr ().accept_vis (*this); for (auto ¶m : expr.get_arguments ()) param->accept_vis (*this); @@ -463,7 +479,7 @@ PrivacyReporter::visit (HIR::CallExpr &expr) void PrivacyReporter::visit (HIR::MethodCallExpr &expr) { - expr.get_receiver ()->accept_vis (*this); + expr.get_receiver ().accept_vis (*this); for (auto ¶m : expr.get_arguments ()) param->accept_vis (*this); @@ -472,7 +488,7 @@ PrivacyReporter::visit (HIR::MethodCallExpr &expr) void PrivacyReporter::visit (HIR::FieldAccessExpr &expr) { - expr.get_receiver_expr ()->accept_vis (*this); + expr.get_receiver_expr ().accept_vis (*this); // FIXME: We should also check if the field is public? } @@ -489,9 +505,8 @@ PrivacyReporter::visit (HIR::BlockExpr &expr) for (auto &stmt : expr.get_statements ()) stmt->accept_vis (*this); - auto &last_expr = expr.get_final_expr (); - if (last_expr) - last_expr->accept_vis (*this); + if (expr.has_final_expr ()) + expr.get_final_expr ().accept_vis (*this); } void @@ -501,28 +516,27 @@ PrivacyReporter::visit (HIR::ContinueExpr &) void PrivacyReporter::visit (HIR::BreakExpr &expr) { - auto &break_expr = expr.get_expr (); - if (break_expr) - break_expr->accept_vis (*this); + if (expr.has_break_expr ()) + expr.get_expr ().accept_vis (*this); } void PrivacyReporter::visit (HIR::RangeFromToExpr &expr) { - expr.get_from_expr ()->accept_vis (*this); - expr.get_to_expr ()->accept_vis (*this); + expr.get_from_expr ().accept_vis (*this); + expr.get_to_expr ().accept_vis (*this); } void PrivacyReporter::visit (HIR::RangeFromExpr &expr) { - expr.get_from_expr ()->accept_vis (*this); + expr.get_from_expr ().accept_vis (*this); } void PrivacyReporter::visit (HIR::RangeToExpr &expr) { - expr.get_to_expr ()->accept_vis (*this); + expr.get_to_expr ().accept_vis (*this); } void @@ -532,8 +546,8 @@ PrivacyReporter::visit (HIR::RangeFullExpr &) void PrivacyReporter::visit (HIR::RangeFromToInclExpr &expr) { - expr.get_from_expr ()->accept_vis (*this); - expr.get_to_expr ()->accept_vis (*this); + expr.get_from_expr ().accept_vis (*this); + expr.get_to_expr ().accept_vis (*this); } void @@ -545,70 +559,55 @@ PrivacyReporter::visit (HIR::RangeToInclExpr &) void PrivacyReporter::visit (HIR::ReturnExpr &expr) { - if (expr.get_expr ()) - expr.get_expr ()->accept_vis (*this); + if (expr.has_expr ()) + expr.get_expr ().accept_vis (*this); } void PrivacyReporter::visit (HIR::UnsafeBlockExpr &expr) { - expr.get_block_expr ()->accept_vis (*this); + expr.get_block_expr ().accept_vis (*this); } void PrivacyReporter::visit (HIR::LoopExpr &expr) { - expr.get_loop_block ()->accept_vis (*this); + expr.get_loop_block ().accept_vis (*this); } void PrivacyReporter::visit (HIR::WhileLoopExpr &expr) { - expr.get_predicate_expr ()->accept_vis (*this); - expr.get_loop_block ()->accept_vis (*this); + expr.get_predicate_expr ().accept_vis (*this); + expr.get_loop_block ().accept_vis (*this); } void PrivacyReporter::visit (HIR::WhileLetLoopExpr &expr) { - expr.get_cond ()->accept_vis (*this); - expr.get_loop_block ()->accept_vis (*this); + expr.get_cond ().accept_vis (*this); + expr.get_loop_block ().accept_vis (*this); } void PrivacyReporter::visit (HIR::IfExpr &expr) { - expr.get_if_condition ()->accept_vis (*this); - expr.get_if_block ()->accept_vis (*this); + expr.get_if_condition ().accept_vis (*this); + expr.get_if_block ().accept_vis (*this); } void PrivacyReporter::visit (HIR::IfExprConseqElse &expr) { - expr.get_if_condition ()->accept_vis (*this); - expr.get_if_block ()->accept_vis (*this); - expr.get_else_block ()->accept_vis (*this); -} - -void -PrivacyReporter::visit (HIR::IfLetExpr &) -{ - // TODO: We need to visit the if_let_expr - // TODO: We need to visit the block as well -} - -void -PrivacyReporter::visit (HIR::IfLetExprConseqElse &) -{ - // TODO: We need to visit the if_let_expr - // TODO: We need to visit the if_block as well - // TODO: We need to visit the else_block as well + expr.get_if_condition ().accept_vis (*this); + expr.get_if_block ().accept_vis (*this); + expr.get_else_block ().accept_vis (*this); } void PrivacyReporter::visit (HIR::MatchExpr &expr) { - expr.get_scrutinee_expr ()->accept_vis (*this); + expr.get_scrutinee_expr ().accept_vis (*this); } void @@ -651,9 +650,9 @@ void PrivacyReporter::visit (HIR::Function &function) { for (auto ¶m : function.get_function_params ()) - check_type_privacy (param.get_type ().get ()); + check_type_privacy (param.get_type ()); - function.get_definition ()->accept_vis (*this); + function.get_definition ().accept_vis (*this); } void @@ -710,14 +709,14 @@ void PrivacyReporter::visit (HIR::ConstantItem &const_item) { // TODO: We need to visit the type - const_item.get_expr ()->accept_vis (*this); + const_item.get_expr ().accept_vis (*this); } void PrivacyReporter::visit (HIR::StaticItem &static_item) { // TODO: We need to visit the type - static_item.get_expr ()->accept_vis (*this); + static_item.get_expr ().accept_vis (*this); } void @@ -750,17 +749,17 @@ PrivacyReporter::visit (HIR::EmptyStmt &) void PrivacyReporter::visit (HIR::LetStmt &stmt) { - if (stmt.get_type ()) - check_type_privacy (stmt.get_type ().get ()); + if (stmt.has_type ()) + check_type_privacy (stmt.get_type ()); - if (stmt.get_init_expr ()) - stmt.get_init_expr ()->accept_vis (*this); + if (stmt.has_init_expr ()) + stmt.get_init_expr ().accept_vis (*this); } void PrivacyReporter::visit (HIR::ExprStmt &stmt) { - stmt.get_expr ()->accept_vis (*this); + stmt.get_expr ().accept_vis (*this); } } // namespace Privacy diff --git a/gcc/rust/checks/errors/privacy/rust-privacy-reporter.h b/gcc/rust/checks/errors/privacy/rust-privacy-reporter.h index 6f2937c..5111a3e 100644 --- a/gcc/rust/checks/errors/privacy/rust-privacy-reporter.h +++ b/gcc/rust/checks/errors/privacy/rust-privacy-reporter.h @@ -75,7 +75,7 @@ types * @param type Reference to an explicit type used in a statement, expression * or parameter */ - void check_type_privacy (const HIR::Type *type); + void check_type_privacy (const HIR::Type &type); virtual void visit (HIR::StructExprFieldIdentifier &field); virtual void visit (HIR::StructExprFieldIdentifierValue &field); @@ -121,11 +121,10 @@ types virtual void visit (HIR::WhileLetLoopExpr &expr); virtual void visit (HIR::IfExpr &expr); virtual void visit (HIR::IfExprConseqElse &expr); - virtual void visit (HIR::IfLetExpr &expr); - virtual void visit (HIR::IfLetExprConseqElse &expr); virtual void visit (HIR::MatchExpr &expr); virtual void visit (HIR::AwaitExpr &expr); virtual void visit (HIR::AsyncBlockExpr &expr); + virtual void visit (HIR::InlineAsm &expr); virtual void visit (HIR::EnumItemTuple &); virtual void visit (HIR::EnumItemStruct &); diff --git a/gcc/rust/checks/errors/privacy/rust-reachability.cc b/gcc/rust/checks/errors/privacy/rust-reachability.cc index 9e0cb82..1e57674 100644 --- a/gcc/rust/checks/errors/privacy/rust-reachability.cc +++ b/gcc/rust/checks/errors/privacy/rust-reachability.cc @@ -132,7 +132,7 @@ ReachabilityVisitor::visit (HIR::StructStruct &struct_item) { for (auto &field : struct_item.get_fields ()) if (field.get_visibility ().is_public ()) - ctx.update_reachability (field.get_field_type ()->get_mappings (), + ctx.update_reachability (field.get_field_type ().get_mappings (), struct_reach); } diff --git a/gcc/rust/checks/errors/privacy/rust-visibility-resolver.cc b/gcc/rust/checks/errors/privacy/rust-visibility-resolver.cc index 464ce86..f0da745 100644 --- a/gcc/rust/checks/errors/privacy/rust-visibility-resolver.cc +++ b/gcc/rust/checks/errors/privacy/rust-visibility-resolver.cc @@ -20,6 +20,10 @@ #include "rust-ast.h" #include "rust-hir.h" #include "rust-hir-item.h" +#include "rust-immutable-name-resolution-context.h" + +// for flag_name_resolution_2_0 +#include "options.h" namespace Rust { namespace Privacy { @@ -61,7 +65,22 @@ VisibilityResolver::resolve_module_path (const HIR::SimplePath &restriction, "cannot use non-module path as privacy restrictor"); NodeId ref_node_id = UNKNOWN_NODEID; - if (!resolver.lookup_resolved_name (ast_node_id, &ref_node_id)) + if (flag_name_resolution_2_0) + { + auto &nr_ctx + = Resolver2_0::ImmutableNameResolutionContext::get ().resolver (); + + if (auto id = nr_ctx.lookup (ast_node_id)) + { + ref_node_id = *id; + } + else + { + invalid_path.emit (); + return false; + } + } + else if (!resolver.lookup_resolved_name (ast_node_id, &ref_node_id)) { invalid_path.emit (); return false; diff --git a/gcc/rust/checks/errors/rust-ast-validation.cc b/gcc/rust/checks/errors/rust-ast-validation.cc index 7938286..59b2805 100644 --- a/gcc/rust/checks/errors/rust-ast-validation.cc +++ b/gcc/rust/checks/errors/rust-ast-validation.cc @@ -56,7 +56,7 @@ ASTValidation::visit (AST::LoopLabel &label) void ASTValidation::visit (AST::ConstantItem &const_item) { - if (!const_item.has_expr () && context.back () != Context::TRAIT_IMPL) + if (!const_item.has_expr () && ctx.peek () != Kind::TRAIT_IMPL) { rust_error_at (const_item.get_locus (), "associated constant in %<impl%> without body"); @@ -82,23 +82,19 @@ ASTValidation::visit (AST::Function &function) "functions cannot be both %<const%> and %<async%>"); if (qualifiers.is_const () - && (context.back () == Context::TRAIT_IMPL - || context.back () == Context::TRAIT)) + && (ctx.peek () == Kind::TRAIT_IMPL || ctx.peek () == Kind::TRAIT)) rust_error_at (function.get_locus (), ErrorCode::E0379, "functions in traits cannot be declared %<const%>"); // may change soon if (qualifiers.is_async () - && (context.back () == Context::TRAIT_IMPL - || context.back () == Context::TRAIT)) + && (ctx.peek () == Kind::TRAIT_IMPL || ctx.peek () == Kind::TRAIT)) rust_error_at (function.get_locus (), ErrorCode::E0706, "functions in traits cannot be declared %<async%>"); // if not an associated function but has a self parameter - if (context.back () != Context::TRAIT - && context.back () != Context::TRAIT_IMPL - && context.back () != Context::INHERENT_IMPL - && function.has_self_param ()) + if (ctx.peek () != Kind::TRAIT && ctx.peek () != Kind::TRAIT_IMPL + && ctx.peek () != Kind::INHERENT_IMPL && function.has_self_param ()) rust_error_at ( function.get_self_param ().get_locus (), "%<self%> parameter is only allowed in associated functions"); @@ -140,11 +136,11 @@ ASTValidation::visit (AST::Function &function) { if (!function.has_body ()) { - if (context.back () == Context::INHERENT_IMPL - || context.back () == Context::TRAIT_IMPL) + if (ctx.peek () == Kind::INHERENT_IMPL + || ctx.peek () == Kind::TRAIT_IMPL) rust_error_at (function.get_locus (), "associated function in %<impl%> without body"); - else if (context.back () != Context::TRAIT) + else if (ctx.peek () != Kind::TRAIT) rust_error_at (function.get_locus (), "free function without a body"); } diff --git a/gcc/rust/checks/errors/rust-const-checker.cc b/gcc/rust/checks/errors/rust-const-checker.cc index 2beee21..97b3528 100644 --- a/gcc/rust/checks/errors/rust-const-checker.cc +++ b/gcc/rust/checks/errors/rust-const-checker.cc @@ -22,6 +22,10 @@ #include "rust-hir-stmt.h" #include "rust-hir-item.h" #include "rust-system.h" +#include "rust-immutable-name-resolution-context.h" + +// for flag_name_resolution_2_0 +#include "options.h" namespace Rust { namespace HIR { @@ -157,72 +161,72 @@ ConstChecker::visit (LiteralExpr &) void ConstChecker::visit (BorrowExpr &expr) { - expr.get_expr ()->accept_vis (*this); + expr.get_expr ().accept_vis (*this); } void ConstChecker::visit (DereferenceExpr &expr) { - expr.get_expr ()->accept_vis (*this); + expr.get_expr ().accept_vis (*this); } void ConstChecker::visit (ErrorPropagationExpr &expr) { - expr.get_expr ()->accept_vis (*this); + expr.get_expr ().accept_vis (*this); } void ConstChecker::visit (NegationExpr &expr) { - expr.get_expr ()->accept_vis (*this); + expr.get_expr ().accept_vis (*this); } void ConstChecker::visit (ArithmeticOrLogicalExpr &expr) { - expr.get_lhs ()->accept_vis (*this); - expr.get_rhs ()->accept_vis (*this); + expr.get_lhs ().accept_vis (*this); + expr.get_rhs ().accept_vis (*this); } void ConstChecker::visit (ComparisonExpr &expr) { - expr.get_lhs ()->accept_vis (*this); - expr.get_rhs ()->accept_vis (*this); + expr.get_lhs ().accept_vis (*this); + expr.get_rhs ().accept_vis (*this); } void ConstChecker::visit (LazyBooleanExpr &expr) { - expr.get_lhs ()->accept_vis (*this); - expr.get_rhs ()->accept_vis (*this); + expr.get_lhs ().accept_vis (*this); + expr.get_rhs ().accept_vis (*this); } void ConstChecker::visit (TypeCastExpr &expr) { - expr.get_expr ()->accept_vis (*this); + expr.get_expr ().accept_vis (*this); } void ConstChecker::visit (AssignmentExpr &expr) { - expr.get_lhs ()->accept_vis (*this); - expr.get_rhs ()->accept_vis (*this); + expr.get_lhs ().accept_vis (*this); + expr.get_rhs ().accept_vis (*this); } void ConstChecker::visit (CompoundAssignmentExpr &expr) { - expr.get_lhs ()->accept_vis (*this); - expr.get_rhs ()->accept_vis (*this); + expr.get_lhs ().accept_vis (*this); + expr.get_rhs ().accept_vis (*this); } void ConstChecker::visit (GroupedExpr &expr) { - expr.get_expr_in_parens ()->accept_vis (*this); + expr.get_expr_in_parens ().accept_vis (*this); } void @@ -235,11 +239,11 @@ ConstChecker::visit (ArrayElemsValues &elems) void ConstChecker::visit (ArrayElemsCopied &elems) { - elems.get_elem_to_copy ()->accept_vis (*this); + elems.get_elem_to_copy ().accept_vis (*this); const_context.enter (elems.get_mappings ().get_hirid ()); - elems.get_num_copies_expr ()->accept_vis (*this); + elems.get_num_copies_expr ().accept_vis (*this); const_context.exit (); } @@ -247,14 +251,14 @@ ConstChecker::visit (ArrayElemsCopied &elems) void ConstChecker::visit (ArrayExpr &expr) { - expr.get_internal_elements ()->accept_vis (*this); + expr.get_internal_elements ().accept_vis (*this); } void ConstChecker::visit (ArrayIndexExpr &expr) { - expr.get_array_expr ()->accept_vis (*this); - expr.get_index_expr ()->accept_vis (*this); + expr.get_array_expr ().accept_vis (*this); + expr.get_index_expr ().accept_vis (*this); } void @@ -267,7 +271,7 @@ ConstChecker::visit (TupleExpr &expr) void ConstChecker::visit (TupleIndexExpr &expr) { - expr.get_tuple_expr ()->accept_vis (*this); + expr.get_tuple_expr ().accept_vis (*this); } void @@ -281,13 +285,13 @@ ConstChecker::visit (StructExprFieldIdentifier &) void ConstChecker::visit (StructExprFieldIdentifierValue &field) { - field.get_value ()->accept_vis (*this); + field.get_value ().accept_vis (*this); } void ConstChecker::visit (StructExprFieldIndexValue &field) { - field.get_value ()->accept_vis (*this); + field.get_value ().accept_vis (*this); } void @@ -348,14 +352,24 @@ ConstChecker::check_function_call (HirId fn_id, location_t locus) void ConstChecker::visit (CallExpr &expr) { - if (!expr.get_fnexpr ()) + if (!expr.has_fnexpr ()) return; - NodeId ast_node_id = expr.get_fnexpr ()->get_mappings ().get_nodeid (); + NodeId ast_node_id = expr.get_fnexpr ().get_mappings ().get_nodeid (); NodeId ref_node_id; + if (flag_name_resolution_2_0) + { + auto &nr_ctx + = Resolver2_0::ImmutableNameResolutionContext::get ().resolver (); + + if (auto id = nr_ctx.lookup (ast_node_id)) + ref_node_id = *id; + else + return; + } // We don't care about types here - if (!resolver.lookup_resolved_name (ast_node_id, &ref_node_id)) + else if (!resolver.lookup_resolved_name (ast_node_id, &ref_node_id)) return; if (auto definition_id = mappings.lookup_node_to_hir (ref_node_id)) @@ -374,7 +388,7 @@ ConstChecker::visit (CallExpr &expr) void ConstChecker::visit (MethodCallExpr &expr) { - expr.get_receiver ()->accept_vis (*this); + expr.get_receiver ().accept_vis (*this); for (auto &arg : expr.get_arguments ()) arg->accept_vis (*this); @@ -383,13 +397,13 @@ ConstChecker::visit (MethodCallExpr &expr) void ConstChecker::visit (FieldAccessExpr &expr) { - expr.get_receiver_expr ()->accept_vis (*this); + expr.get_receiver_expr ().accept_vis (*this); } void ConstChecker::visit (ClosureExpr &expr) { - expr.get_expr ()->accept_vis (*this); + expr.get_expr ().accept_vis (*this); } void @@ -399,7 +413,7 @@ ConstChecker::visit (BlockExpr &expr) stmt->accept_vis (*this); if (expr.has_expr ()) - expr.get_final_expr ()->accept_vis (*this); + expr.get_final_expr ().accept_vis (*this); } void @@ -410,26 +424,26 @@ void ConstChecker::visit (BreakExpr &expr) { if (expr.has_break_expr ()) - expr.get_expr ()->accept_vis (*this); + expr.get_expr ().accept_vis (*this); } void ConstChecker::visit (RangeFromToExpr &expr) { - expr.get_from_expr ()->accept_vis (*this); - expr.get_to_expr ()->accept_vis (*this); + expr.get_from_expr ().accept_vis (*this); + expr.get_to_expr ().accept_vis (*this); } void ConstChecker::visit (RangeFromExpr &expr) { - expr.get_from_expr ()->accept_vis (*this); + expr.get_from_expr ().accept_vis (*this); } void ConstChecker::visit (RangeToExpr &expr) { - expr.get_to_expr ()->accept_vis (*this); + expr.get_to_expr ().accept_vis (*this); } void @@ -439,8 +453,8 @@ ConstChecker::visit (RangeFullExpr &) void ConstChecker::visit (RangeFromToInclExpr &expr) { - expr.get_from_expr ()->accept_vis (*this); - expr.get_to_expr ()->accept_vis (*this); + expr.get_from_expr ().accept_vis (*this); + expr.get_to_expr ().accept_vis (*this); } void @@ -453,73 +467,57 @@ void ConstChecker::visit (ReturnExpr &expr) { if (expr.has_return_expr ()) - expr.get_expr ()->accept_vis (*this); + expr.get_expr ().accept_vis (*this); } void ConstChecker::visit (UnsafeBlockExpr &expr) { - expr.get_block_expr ()->accept_vis (*this); + expr.get_block_expr ().accept_vis (*this); } void ConstChecker::visit (LoopExpr &expr) { - expr.get_loop_block ()->accept_vis (*this); + expr.get_loop_block ().accept_vis (*this); } void ConstChecker::visit (WhileLoopExpr &expr) { - expr.get_predicate_expr ()->accept_vis (*this); - expr.get_loop_block ()->accept_vis (*this); + expr.get_predicate_expr ().accept_vis (*this); + expr.get_loop_block ().accept_vis (*this); } void ConstChecker::visit (WhileLetLoopExpr &expr) { - expr.get_cond ()->accept_vis (*this); - expr.get_loop_block ()->accept_vis (*this); + expr.get_cond ().accept_vis (*this); + expr.get_loop_block ().accept_vis (*this); } void ConstChecker::visit (IfExpr &expr) { - expr.get_if_condition ()->accept_vis (*this); - expr.get_if_block ()->accept_vis (*this); + expr.get_if_condition ().accept_vis (*this); + expr.get_if_block ().accept_vis (*this); } void ConstChecker::visit (IfExprConseqElse &expr) { - expr.get_if_condition ()->accept_vis (*this); - expr.get_if_block ()->accept_vis (*this); - expr.get_else_block ()->accept_vis (*this); -} - -void -ConstChecker::visit (IfLetExpr &expr) -{ - expr.get_scrutinee_expr ()->accept_vis (*this); - expr.get_if_block ()->accept_vis (*this); -} - -void -ConstChecker::visit (IfLetExprConseqElse &expr) -{ - expr.get_scrutinee_expr ()->accept_vis (*this); - expr.get_if_block ()->accept_vis (*this); - - // TODO: Visit else expression + expr.get_if_condition ().accept_vis (*this); + expr.get_if_block ().accept_vis (*this); + expr.get_else_block ().accept_vis (*this); } void ConstChecker::visit (MatchExpr &expr) { - expr.get_scrutinee_expr ()->accept_vis (*this); + expr.get_scrutinee_expr ().accept_vis (*this); for (auto &match_arm : expr.get_match_cases ()) - match_arm.get_expr ()->accept_vis (*this); + match_arm.get_expr ().accept_vis (*this); } void @@ -592,9 +590,9 @@ ConstChecker::visit (Function &function) ConstGenericCtx::Function); for (auto ¶m : function.get_function_params ()) - param.get_type ()->accept_vis (*this); + param.get_type ().accept_vis (*this); - function.get_definition ()->accept_vis (*this); + function.get_definition ().accept_vis (*this); if (const_fn) const_context.exit (); @@ -638,7 +636,7 @@ ConstChecker::visit (EnumItemDiscriminant &item) { const_context.enter (item.get_mappings ().get_hirid ()); - item.get_discriminant_expression ()->accept_vis (*this); + item.get_discriminant_expression ().accept_vis (*this); const_context.exit (); } @@ -662,7 +660,7 @@ ConstChecker::visit (ConstantItem &const_item) { const_context.enter (const_item.get_mappings ().get_hirid ()); - const_item.get_expr ()->accept_vis (*this); + const_item.get_expr ().accept_vis (*this); const_context.exit (); } @@ -672,7 +670,7 @@ ConstChecker::visit (StaticItem &static_item) { const_context.enter (static_item.get_mappings ().get_hirid ()); - static_item.get_expr ()->accept_vis (*this); + static_item.get_expr ().accept_vis (*this); const_context.exit (); } @@ -680,15 +678,15 @@ ConstChecker::visit (StaticItem &static_item) void ConstChecker::visit (TraitItemFunc &item) { - if (item.has_block_defined ()) - item.get_block_expr ()->accept_vis (*this); + if (item.has_definition ()) + item.get_block_expr ().accept_vis (*this); } void ConstChecker::visit (TraitItemConst &item) { if (item.has_expr ()) - item.get_expr ()->accept_vis (*this); + item.get_expr ().accept_vis (*this); } void @@ -823,13 +821,13 @@ void ConstChecker::visit (LetStmt &stmt) { if (stmt.has_init_expr ()) - stmt.get_init_expr ()->accept_vis (*this); + stmt.get_init_expr ().accept_vis (*this); } void ConstChecker::visit (ExprStmt &stmt) { - stmt.get_expr ()->accept_vis (*this); + stmt.get_expr ().accept_vis (*this); } void @@ -877,7 +875,7 @@ ConstChecker::visit (ArrayType &type) { const_context.enter (type.get_mappings ().get_hirid ()); - type.get_size_expr ()->accept_vis (*this); + type.get_size_expr ().accept_vis (*this); const_context.exit (); } diff --git a/gcc/rust/checks/errors/rust-const-checker.h b/gcc/rust/checks/errors/rust-const-checker.h index 8890761..9a618b8 100644 --- a/gcc/rust/checks/errors/rust-const-checker.h +++ b/gcc/rust/checks/errors/rust-const-checker.h @@ -128,8 +128,6 @@ private: virtual void visit (WhileLetLoopExpr &expr) override; virtual void visit (IfExpr &expr) override; virtual void visit (IfExprConseqElse &expr) override; - virtual void visit (IfLetExpr &expr) override; - virtual void visit (IfLetExprConseqElse &expr) override; virtual void visit (MatchExpr &expr) override; virtual void visit (AwaitExpr &expr) override; virtual void visit (AsyncBlockExpr &expr) override; diff --git a/gcc/rust/checks/errors/rust-feature-gate.cc b/gcc/rust/checks/errors/rust-feature-gate.cc index 580afc9..f3daa61 100644 --- a/gcc/rust/checks/errors/rust-feature-gate.cc +++ b/gcc/rust/checks/errors/rust-feature-gate.cc @@ -21,6 +21,7 @@ #include "rust-attribute-values.h" #include "rust-ast-visitor.h" #include "rust-feature.h" +#include "rust-ast-full.h" namespace Rust { @@ -75,16 +76,17 @@ FeatureGate::gate (Feature::Name name, location_t loc, if (!valid_features.count (name)) { auto feature = Feature::create (name); - auto issue = feature.issue (); - if (issue > 0) + if (auto issue = feature.issue ()) { + auto issue_number = issue.value (); const char *fmt_str = "%s. see issue %u " "<https://github.com/rust-lang/rust/issues/%u> for more " "information. add `#![feature(%s)]` to the crate attributes to " "enable."; rust_error_at (loc, ErrorCode::E0658, fmt_str, error_msg.c_str (), - issue, issue, feature.as_string ().c_str ()); + issue_number, issue_number, + feature.as_string ().c_str ()); } else { @@ -169,7 +171,16 @@ FeatureGate::visit (AST::TraitImpl &impl) "negative_impls are not yet implemented"); AST::DefaultASTVisitor::visit (impl); -}; +} + +void +FeatureGate::visit (AST::Trait &trait) +{ + if (trait.is_auto ()) + gate (Feature::Name::AUTO_TRAITS, trait.get_locus (), + "auto traits are experimental and possibly buggy"); + AST::DefaultASTVisitor::visit (trait); +} void FeatureGate::visit (AST::BoxExpr &expr) @@ -217,4 +228,12 @@ FeatureGate::visit (AST::RangePattern &pattern) "exclusive range pattern syntax is experimental"); } +void +FeatureGate::visit (AST::UseTreeGlob &use) +{ + // At the moment, UseTrees do not have outer attributes, but they should. we + // need to eventually gate `#[prelude_import]` on use-trees based on the + // #[feature(prelude_import)] +} + } // namespace Rust diff --git a/gcc/rust/checks/errors/rust-feature-gate.h b/gcc/rust/checks/errors/rust-feature-gate.h index 31c2ed6..f1011e5 100644 --- a/gcc/rust/checks/errors/rust-feature-gate.h +++ b/gcc/rust/checks/errors/rust-feature-gate.h @@ -20,7 +20,6 @@ #define RUST_FEATURE_GATE_H #include "rust-ast-visitor.h" -#include "rust-ast-full.h" #include "rust-feature.h" namespace Rust { @@ -35,153 +34,19 @@ public: void check (AST::Crate &crate); void visit (AST::Crate &crate) override; - void visit (AST::Token &tok) override {} - void visit (AST::DelimTokenTree &delim_tok_tree) override {} - void visit (AST::AttrInputMetaItemContainer &input) override {} - void visit (AST::IdentifierExpr &ident_expr) override {} - void visit (AST::Lifetime &lifetime) override {} void visit (AST::LifetimeParam &lifetime_param) override; void visit (AST::ConstGenericParam &const_param) override; - void visit (AST::PathInExpression &path) override {} - void visit (AST::TypePathSegment &segment) override {} - void visit (AST::TypePathSegmentGeneric &segment) override {} - void visit (AST::TypePathSegmentFunction &segment) override {} - void visit (AST::TypePath &path) override {} - void visit (AST::QualifiedPathInExpression &path) override {} - void visit (AST::QualifiedPathInType &path) override {} - void visit (AST::LiteralExpr &expr) override {} - void visit (AST::AttrInputLiteral &attr_input) override {} - void visit (AST::AttrInputMacro &attr_input) override {} - void visit (AST::MetaItemLitExpr &meta_item) override {} - void visit (AST::MetaItemPathLit &meta_item) override {} void visit (AST::BorrowExpr &expr) override; - void visit (AST::DereferenceExpr &expr) override {} - void visit (AST::ErrorPropagationExpr &expr) override {} - void visit (AST::NegationExpr &expr) override {} - void visit (AST::ArithmeticOrLogicalExpr &expr) override {} - void visit (AST::ComparisonExpr &expr) override {} - void visit (AST::LazyBooleanExpr &expr) override {} - void visit (AST::TypeCastExpr &expr) override {} - void visit (AST::AssignmentExpr &expr) override {} - void visit (AST::CompoundAssignmentExpr &expr) override {} - void visit (AST::GroupedExpr &expr) override {} - void visit (AST::ArrayElemsValues &elems) override {} - void visit (AST::ArrayElemsCopied &elems) override {} - void visit (AST::ArrayExpr &expr) override {} - void visit (AST::ArrayIndexExpr &expr) override {} - void visit (AST::TupleExpr &expr) override {} - void visit (AST::TupleIndexExpr &expr) override {} - void visit (AST::StructExprStruct &expr) override {} - void visit (AST::StructExprFieldIdentifier &field) override {} - void visit (AST::StructExprFieldIdentifierValue &field) override {} - void visit (AST::StructExprFieldIndexValue &field) override {} - void visit (AST::StructExprStructFields &expr) override {} - void visit (AST::StructExprStructBase &expr) override {} - void visit (AST::CallExpr &expr) override {} - void visit (AST::MethodCallExpr &expr) override {} - void visit (AST::FieldAccessExpr &expr) override {} - void visit (AST::ClosureExprInner &expr) override {} - void visit (AST::ClosureExprInnerTyped &expr) override {} - void visit (AST::ContinueExpr &expr) override {} - void visit (AST::BreakExpr &expr) override {} - void visit (AST::RangeFromToExpr &expr) override {} - void visit (AST::RangeFromExpr &expr) override {} - void visit (AST::RangeToExpr &expr) override {} - void visit (AST::RangeFullExpr &expr) override {} - void visit (AST::RangeFromToInclExpr &expr) override {} - void visit (AST::RangeToInclExpr &expr) override {} - void visit (AST::ReturnExpr &expr) override {} void visit (AST::BoxExpr &expr) override; - void visit (AST::UnsafeBlockExpr &expr) override {} - void visit (AST::LoopExpr &expr) override {} - void visit (AST::WhileLoopExpr &expr) override {} - void visit (AST::WhileLetLoopExpr &expr) override {} - void visit (AST::ForLoopExpr &expr) override {} - void visit (AST::IfExpr &expr) override {} - void visit (AST::IfExprConseqElse &expr) override {} - void visit (AST::IfLetExprConseqElse &expr) override {} - void visit (AST::AwaitExpr &expr) override {} - void visit (AST::AsyncBlockExpr &expr) override {} void visit (AST::TypeParam ¶m) override; - void visit (AST::LifetimeWhereClauseItem &item) override {} - void visit (AST::TypeBoundWhereClauseItem &item) override {} - void visit (AST::Module &module) override {} - void visit (AST::ExternCrate &crate) override {} - void visit (AST::UseTreeGlob &use_tree) override {} - void visit (AST::UseTreeList &use_tree) override {} - void visit (AST::UseTreeRebind &use_tree) override {} - void visit (AST::UseDeclaration &use_decl) override {} + void visit (AST::UseTreeGlob &use_tree) override; void visit (AST::Function &function) override; - void visit (AST::TypeAlias &type_alias) override {} - void visit (AST::StructStruct &struct_item) override {} - void visit (AST::TupleStruct &tuple_struct) override {} - void visit (AST::EnumItem &item) override {} - void visit (AST::EnumItemTuple &item) override {} - void visit (AST::EnumItemStruct &item) override {} - void visit (AST::EnumItemDiscriminant &item) override {} - void visit (AST::Enum &enum_item) override {} - void visit (AST::Union &union_item) override {} - void visit (AST::ConstantItem &const_item) override {} - void visit (AST::StaticItem &static_item) override {} - void visit (AST::TraitItemConst &item) override {} - void visit (AST::TraitItemType &item) override {} void visit (AST::TraitImpl &impl) override; - void visit (AST::Trait &trait) override {} + void visit (AST::Trait &trait) override; void visit (AST::ExternalTypeItem &item) override; - void visit (AST::ExternalStaticItem &item) override {} void visit (AST::ExternBlock &block) override; - void visit (AST::MacroMatchFragment &match) override {} - void visit (AST::MacroMatchRepetition &match) override {} - void visit (AST::MacroMatcher &matcher) override {} void visit (AST::MacroRulesDefinition &rules_def) override; - void visit (AST::MacroInvocation ¯o_invoc) override {} - void visit (AST::MetaItemPath &meta_item) override {} - void visit (AST::MetaItemSeq &meta_item) override {} - void visit (AST::MetaWord &meta_item) override {} - void visit (AST::MetaNameValueStr &meta_item) override {} - void visit (AST::MetaListPaths &meta_item) override {} - void visit (AST::MetaListNameValueStr &meta_item) override {} - void visit (AST::LiteralPattern &pattern) override {} - void visit (AST::IdentifierPattern &pattern) override {} - void visit (AST::WildcardPattern &pattern) override {} - void visit (AST::RestPattern &pattern) override {} - void visit (AST::RangePatternBoundLiteral &bound) override {} - void visit (AST::RangePatternBoundPath &bound) override {} - void visit (AST::RangePatternBoundQualPath &bound) override {} void visit (AST::RangePattern &pattern) override; - void visit (AST::ReferencePattern &pattern) override {} - void visit (AST::StructPatternFieldTuplePat &field) override {} - void visit (AST::StructPatternFieldIdentPat &field) override {} - void visit (AST::StructPatternFieldIdent &field) override {} - void visit (AST::StructPattern &pattern) override {} - void visit (AST::TupleStructItemsNoRange &tuple_items) override {} - void visit (AST::TupleStructItemsRange &tuple_items) override {} - void visit (AST::TupleStructPattern &pattern) override {} - void visit (AST::TuplePatternItemsMultiple &tuple_items) override {} - void visit (AST::TuplePatternItemsRanged &tuple_items) override {} - void visit (AST::TuplePattern &pattern) override {} - void visit (AST::GroupedPattern &pattern) override {} - void visit (AST::SlicePattern &pattern) override {} - void visit (AST::AltPattern &pattern) override {} - void visit (AST::EmptyStmt &stmt) override {} - void visit (AST::ExprStmt &stmt) override {} - void visit (AST::TraitBound &bound) override {} - void visit (AST::ImplTraitType &type) override {} - void visit (AST::TraitObjectType &type) override {} - void visit (AST::ParenthesisedType &type) override {} - void visit (AST::ImplTraitTypeOneBound &type) override {} - void visit (AST::TraitObjectTypeOneBound &type) override {} - void visit (AST::TupleType &type) override {} - void visit (AST::NeverType &type) override {} - void visit (AST::RawPointerType &type) override {} - void visit (AST::ReferenceType &type) override {} - void visit (AST::ArrayType &type) override {} - void visit (AST::SliceType &type) override {} - void visit (AST::InferredType &type) override {} - void visit (AST::BareFunctionType &type) override {} - void visit (AST::FunctionParam ¶m) override {} - void visit (AST::VariadicParam ¶m) override {} - void visit (AST::SelfParam ¶m) override {} private: void gate (Feature::Name name, location_t loc, const std::string &error_msg); diff --git a/gcc/rust/checks/errors/rust-feature.cc b/gcc/rust/checks/errors/rust-feature.cc index 917e3b2..25af46c 100644 --- a/gcc/rust/checks/errors/rust-feature.cc +++ b/gcc/rust/checks/errors/rust-feature.cc @@ -17,47 +17,47 @@ // <http://www.gnu.org/licenses/>. #include "rust-feature.h" -#include "rust-session-manager.h" namespace Rust { Feature -Feature::create (Feature::Name name) +Feature::create (Feature::Name f) { - switch (name) + switch (f) { case Feature::Name::ASSOCIATED_TYPE_BOUNDS: return Feature (Feature::Name::ASSOCIATED_TYPE_BOUNDS, Feature::State::ACCEPTED, "associated_type_bounds", - "1.34.0", 52662, tl::nullopt, ""); + "1.34.0", 52662); case Feature::Name::INTRINSICS: - return Feature (Feature::Name::INTRINSICS, Feature::State::ACCEPTED, - "intrinsics", "1.0.0", 0, tl::nullopt, ""); + return Feature (f, Feature::State::ACCEPTED, "intrinsics", "1.0.0"); case Feature::Name::RUSTC_ATTRS: - return Feature (Feature::Name::RUSTC_ATTRS, Feature::State::ACCEPTED, - "rustc_attrs", "1.0.0", 0, tl::nullopt, ""); + return Feature (f, Feature::State::ACCEPTED, "rustc_attrs", "1.0.0"); case Feature::Name::DECL_MACRO: - return Feature (Feature::Name::DECL_MACRO, Feature::State::ACCEPTED, - "decl_macro", "1.0.0", 0, tl::nullopt, ""); + return Feature (f, Feature::State::ACCEPTED, "decl_macro", "1.0.0", + 39412); case Feature::Name::EXTERN_TYPES: - return Feature (Feature::Name::EXTERN_TYPES, Feature::State::ACTIVE, - "extern_types", "1.23.0", 43467, tl::nullopt, ""); + return Feature (f, Feature::State::ACTIVE, "extern_types", "1.23.0", + 43467); case Feature::Name::NEGATIVE_IMPLS: - return Feature (Feature::Name::NEGATIVE_IMPLS, Feature::State::ACTIVE, - "negative_impls", "1.0.0", 68318, tl::nullopt, ""); + return Feature (f, Feature::State::ACTIVE, "negative_impls", "1.0.0", + 68318); case Feature::Name::BOX_SYNTAX: - return Feature (Feature::Name::BOX_SYNTAX, Feature::State::ACTIVE, - "box_syntax", "1.0.0", 49733, tl::nullopt, ""); + return Feature (f, Feature::State::ACTIVE, "box_syntax", "1.0.0", 49733); case Feature::Name::DROPCK_EYEPATCH: - return Feature (Feature::Name::DROPCK_EYEPATCH, Feature::State::ACTIVE, - "dropck_eyepatch", "1.10.0", 34761, tl::nullopt, ""); + return Feature (f, Feature::State::ACTIVE, "dropck_eyepatch", "1.10.0", + 34761); case Feature::Name::RAW_REF_OP: - return Feature (Feature::Name::RAW_REF_OP, Feature::State::ACTIVE, - "raw_ref_op", "1.41.0", 64490, tl::nullopt, ""); + return Feature (f, Feature::State::ACTIVE, "raw_ref_op", "1.41.0", 64490); case Feature::Name::EXCLUSIVE_RANGE_PATTERN: return Feature (Feature::Name::EXCLUSIVE_RANGE_PATTERN, Feature::State::ACTIVE, "exclusive_range_pattern", - "1.11.0", 37854, tl::nullopt, ""); + "1.11.0", 37854); + case Feature::Name::PRELUDE_IMPORT: + return Feature (f, Feature::State::ACTIVE, "prelude_import", "1.0.0"); + case Feature::Name::AUTO_TRAITS: + return Feature (f, Feature::State::ACTIVE, "optin_builtin_traits", + "1.0.0", 13231); default: rust_unreachable (); } @@ -79,6 +79,7 @@ const std::map<std::string, Feature::Name> Feature::name_hash_map = { {"dropck_eyepatch", Feature::Name::DROPCK_EYEPATCH}, {"raw_ref_op", Feature::Name::RAW_REF_OP}, {"exclusive_range_pattern", Feature::Name::EXCLUSIVE_RANGE_PATTERN}, + {"prelude_import", Feature::Name::PRELUDE_IMPORT}, }; // namespace Rust tl::optional<Feature::Name> diff --git a/gcc/rust/checks/errors/rust-feature.h b/gcc/rust/checks/errors/rust-feature.h index 698aac2..e2082c5 100644 --- a/gcc/rust/checks/errors/rust-feature.h +++ b/gcc/rust/checks/errors/rust-feature.h @@ -50,22 +50,24 @@ public: DROPCK_EYEPATCH, RAW_REF_OP, EXCLUSIVE_RANGE_PATTERN, + PRELUDE_IMPORT, }; const std::string &as_string () { return m_name_str; } Name name () { return m_name; } const std::string &description () { return m_description; } State state () { return m_state; } - unsigned issue () { return m_issue; } + tl::optional<unsigned> issue () { return m_issue; } static tl::optional<Name> as_name (const std::string &name); static Feature create (Name name); private: Feature (Name name, State state, const char *name_str, - const char *rustc_since, unsigned issue_number, - const tl::optional<CompileOptions::Edition> &edition, - const char *description) + const char *rustc_since, + tl::optional<unsigned> issue_number = tl::nullopt, + const tl::optional<CompileOptions::Edition> &edition = tl::nullopt, + const char *description = "") : m_state (state), m_name (name), m_name_str (name_str), m_rustc_since (rustc_since), m_issue (issue_number), edition (edition), m_description (description) @@ -75,9 +77,9 @@ private: Name m_name; std::string m_name_str; std::string m_rustc_since; - unsigned m_issue; + tl::optional<unsigned> m_issue; tl::optional<CompileOptions::Edition> edition; - std::string m_description; + std::string m_description; // TODO: Switch to optional? static const std::map<std::string, Name> name_hash_map; }; diff --git a/gcc/rust/checks/errors/rust-hir-pattern-analysis.cc b/gcc/rust/checks/errors/rust-hir-pattern-analysis.cc new file mode 100644 index 0000000..79416b5 --- /dev/null +++ b/gcc/rust/checks/errors/rust-hir-pattern-analysis.cc @@ -0,0 +1,1556 @@ +// Copyright (C) 2020-2024 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +#include "rust-system.h" +#include "rust-hir-pattern-analysis.h" +#include "rust-diagnostics.h" +#include "rust-hir-full-decls.h" +#include "rust-hir-path.h" +#include "rust-hir-pattern.h" +#include "rust-hir.h" +#include "rust-mapping-common.h" +#include "rust-system.h" +#include "rust-tyty.h" +#include "rust-immutable-name-resolution-context.h" + +// for flag_name_resolution_2_0 +#include "options.h" + +namespace Rust { +namespace Analysis { + +PatternChecker::PatternChecker () + : tyctx (*Resolver::TypeCheckContext::get ()), + resolver (*Resolver::Resolver::get ()), + mappings (Analysis::Mappings::get ()) +{} + +void +PatternChecker::go (HIR::Crate &crate) +{ + rust_debug ("started pattern check"); + for (auto &item : crate.get_items ()) + item->accept_vis (*this); + rust_debug ("finished pattern check"); +} + +void +PatternChecker::visit (Lifetime &) +{} + +void +PatternChecker::visit (LifetimeParam &) +{} + +void +PatternChecker::visit (PathInExpression &path) +{} + +void +PatternChecker::visit (TypePathSegment &) +{} + +void +PatternChecker::visit (TypePathSegmentGeneric &) +{} + +void +PatternChecker::visit (TypePathSegmentFunction &) +{} + +void +PatternChecker::visit (TypePath &) +{} + +void +PatternChecker::visit (QualifiedPathInExpression &) +{} + +void +PatternChecker::visit (QualifiedPathInType &) +{} + +void +PatternChecker::visit (LiteralExpr &) +{} + +void +PatternChecker::visit (BorrowExpr &expr) +{ + expr.get_expr ().accept_vis (*this); +} + +void +PatternChecker::visit (DereferenceExpr &expr) +{ + expr.get_expr ().accept_vis (*this); +} + +void +PatternChecker::visit (ErrorPropagationExpr &expr) +{ + expr.get_expr ().accept_vis (*this); +} + +void +PatternChecker::visit (NegationExpr &expr) +{ + expr.get_expr ().accept_vis (*this); +} + +void +PatternChecker::visit (ArithmeticOrLogicalExpr &expr) +{ + expr.get_lhs ().accept_vis (*this); + expr.get_rhs ().accept_vis (*this); +} + +void +PatternChecker::visit (ComparisonExpr &expr) +{ + expr.get_lhs ().accept_vis (*this); + expr.get_rhs ().accept_vis (*this); +} + +void +PatternChecker::visit (LazyBooleanExpr &expr) +{ + expr.get_lhs ().accept_vis (*this); + expr.get_rhs ().accept_vis (*this); +} + +void +PatternChecker::visit (TypeCastExpr &expr) +{ + expr.get_expr ().accept_vis (*this); +} + +void +PatternChecker::visit (AssignmentExpr &expr) +{ + expr.get_lhs ().accept_vis (*this); + expr.get_rhs ().accept_vis (*this); +} + +void +PatternChecker::visit (CompoundAssignmentExpr &expr) +{ + expr.get_lhs ().accept_vis (*this); + expr.get_rhs ().accept_vis (*this); +} + +void +PatternChecker::visit (GroupedExpr &expr) +{ + expr.get_expr_in_parens ().accept_vis (*this); +} + +void +PatternChecker::visit (ArrayElemsValues &elems) +{ + for (auto &elem : elems.get_values ()) + elem->accept_vis (*this); +} + +void +PatternChecker::visit (ArrayElemsCopied &elems) +{ + elems.get_elem_to_copy ().accept_vis (*this); +} + +void +PatternChecker::visit (ArrayExpr &expr) +{ + expr.get_internal_elements ().accept_vis (*this); +} + +void +PatternChecker::visit (ArrayIndexExpr &expr) +{ + expr.get_array_expr ().accept_vis (*this); + expr.get_index_expr ().accept_vis (*this); +} + +void +PatternChecker::visit (TupleExpr &expr) +{ + for (auto &elem : expr.get_tuple_elems ()) + elem->accept_vis (*this); +} + +void +PatternChecker::visit (TupleIndexExpr &expr) +{ + expr.get_tuple_expr ().accept_vis (*this); +} + +void +PatternChecker::visit (StructExprStruct &) +{} + +void +PatternChecker::visit (StructExprFieldIdentifier &) +{} + +void +PatternChecker::visit (StructExprFieldIdentifierValue &field) +{ + field.get_value ().accept_vis (*this); +} + +void +PatternChecker::visit (StructExprFieldIndexValue &field) +{ + field.get_value ().accept_vis (*this); +} + +void +PatternChecker::visit (StructExprStructFields &expr) +{ + for (auto &field : expr.get_fields ()) + field->accept_vis (*this); +} + +void +PatternChecker::visit (StructExprStructBase &) +{} + +void +PatternChecker::visit (CallExpr &expr) +{ + if (!expr.has_fnexpr ()) + return; + + NodeId ast_node_id = expr.get_fnexpr ().get_mappings ().get_nodeid (); + NodeId ref_node_id; + if (flag_name_resolution_2_0) + { + auto &nr_ctx + = Resolver2_0::ImmutableNameResolutionContext::get ().resolver (); + + if (auto id = nr_ctx.lookup (ast_node_id)) + ref_node_id = *id; + else + return; + } + else if (!resolver.lookup_resolved_name (ast_node_id, &ref_node_id)) + return; + + if (auto definition_id = mappings.lookup_node_to_hir (ref_node_id)) + { + if (expr.has_params ()) + for (auto &arg : expr.get_arguments ()) + arg->accept_vis (*this); + } + else + { + rust_unreachable (); + } +} + +void +PatternChecker::visit (MethodCallExpr &expr) +{ + expr.get_receiver ().accept_vis (*this); + + for (auto &arg : expr.get_arguments ()) + arg->accept_vis (*this); +} + +void +PatternChecker::visit (FieldAccessExpr &expr) +{ + expr.get_receiver_expr ().accept_vis (*this); +} + +void +PatternChecker::visit (ClosureExpr &expr) +{ + expr.get_expr ().accept_vis (*this); +} + +void +PatternChecker::visit (BlockExpr &expr) +{ + for (auto &stmt : expr.get_statements ()) + stmt->accept_vis (*this); + + if (expr.has_expr ()) + expr.get_final_expr ().accept_vis (*this); +} + +void +PatternChecker::visit (ContinueExpr &) +{} + +void +PatternChecker::visit (BreakExpr &expr) +{ + if (expr.has_break_expr ()) + expr.get_expr ().accept_vis (*this); +} + +void +PatternChecker::visit (RangeFromToExpr &expr) +{ + expr.get_from_expr ().accept_vis (*this); + expr.get_to_expr ().accept_vis (*this); +} + +void +PatternChecker::visit (RangeFromExpr &expr) +{ + expr.get_from_expr ().accept_vis (*this); +} + +void +PatternChecker::visit (RangeToExpr &expr) +{ + expr.get_to_expr ().accept_vis (*this); +} + +void +PatternChecker::visit (RangeFullExpr &) +{} + +void +PatternChecker::visit (RangeFromToInclExpr &expr) +{ + expr.get_from_expr ().accept_vis (*this); + expr.get_to_expr ().accept_vis (*this); +} + +void +PatternChecker::visit (RangeToInclExpr &expr) +{ + expr.get_to_expr ().accept_vis (*this); +} + +void +PatternChecker::visit (ReturnExpr &expr) +{ + if (expr.has_return_expr ()) + expr.get_expr ().accept_vis (*this); +} + +void +PatternChecker::visit (UnsafeBlockExpr &expr) +{ + expr.get_block_expr ().accept_vis (*this); +} + +void +PatternChecker::visit (LoopExpr &expr) +{ + expr.get_loop_block ().accept_vis (*this); +} + +void +PatternChecker::visit (WhileLoopExpr &expr) +{ + expr.get_predicate_expr ().accept_vis (*this); + expr.get_loop_block ().accept_vis (*this); +} + +void +PatternChecker::visit (WhileLetLoopExpr &expr) +{ + expr.get_cond ().accept_vis (*this); + expr.get_loop_block ().accept_vis (*this); +} + +void +PatternChecker::visit (IfExpr &expr) +{ + expr.get_if_condition ().accept_vis (*this); + expr.get_if_block ().accept_vis (*this); +} + +void +PatternChecker::visit (IfExprConseqElse &expr) +{ + expr.get_if_condition ().accept_vis (*this); + expr.get_if_block ().accept_vis (*this); + expr.get_else_block ().accept_vis (*this); +} + +void +PatternChecker::visit (MatchExpr &expr) +{ + expr.get_scrutinee_expr ().accept_vis (*this); + + for (auto &match_arm : expr.get_match_cases ()) + match_arm.get_expr ().accept_vis (*this); + + // match expressions are only an entrypoint + TyTy::BaseType *scrutinee_ty; + bool ok = tyctx.lookup_type ( + expr.get_scrutinee_expr ().get_mappings ().get_hirid (), &scrutinee_ty); + rust_assert (ok); + + check_match_usefulness (&tyctx, scrutinee_ty, expr); +} + +void +PatternChecker::visit (AwaitExpr &) +{ + // TODO: Visit expression +} + +void +PatternChecker::visit (AsyncBlockExpr &) +{ + // TODO: Visit block expression +} + +void +PatternChecker::visit (InlineAsm &expr) +{} + +void +PatternChecker::visit (TypeParam &) +{} + +void +PatternChecker::visit (ConstGenericParam &) +{} + +void +PatternChecker::visit (LifetimeWhereClauseItem &) +{} + +void +PatternChecker::visit (TypeBoundWhereClauseItem &) +{} + +void +PatternChecker::visit (Module &module) +{ + for (auto &item : module.get_items ()) + item->accept_vis (*this); +} + +void +PatternChecker::visit (ExternCrate &) +{} + +void +PatternChecker::visit (UseTreeGlob &) +{} + +void +PatternChecker::visit (UseTreeList &) +{} + +void +PatternChecker::visit (UseTreeRebind &) +{} + +void +PatternChecker::visit (UseDeclaration &) +{} + +void +PatternChecker::visit (Function &function) +{ + function.get_definition ().accept_vis (*this); +} + +void +PatternChecker::visit (TypeAlias &) +{} + +void +PatternChecker::visit (StructStruct &) +{} + +void +PatternChecker::visit (TupleStruct &) +{} + +void +PatternChecker::visit (EnumItem &) +{} + +void +PatternChecker::visit (EnumItemTuple &) +{} + +void +PatternChecker::visit (EnumItemStruct &) +{} + +void +PatternChecker::visit (EnumItemDiscriminant &) +{} + +void +PatternChecker::visit (Enum &) +{} + +void +PatternChecker::visit (Union &) +{} + +void +PatternChecker::visit (ConstantItem &const_item) +{ + const_item.get_expr ().accept_vis (*this); +} + +void +PatternChecker::visit (StaticItem &static_item) +{ + static_item.get_expr ().accept_vis (*this); +} + +void +PatternChecker::visit (TraitItemFunc &item) +{ + if (item.has_definition ()) + item.get_block_expr ().accept_vis (*this); +} + +void +PatternChecker::visit (TraitItemConst &item) +{ + if (item.has_expr ()) + item.get_expr ().accept_vis (*this); +} + +void +PatternChecker::visit (TraitItemType &) +{} + +void +PatternChecker::visit (Trait &trait) +{ + for (auto &item : trait.get_trait_items ()) + item->accept_vis (*this); +} + +void +PatternChecker::visit (ImplBlock &impl) +{ + for (auto &item : impl.get_impl_items ()) + item->accept_vis (*this); +} + +void +PatternChecker::visit (ExternalStaticItem &) +{} + +void +PatternChecker::visit (ExternalFunctionItem &) +{} + +void +PatternChecker::visit (ExternalTypeItem &) +{} + +void +PatternChecker::visit (ExternBlock &block) +{ + // FIXME: Do we need to do this? + for (auto &item : block.get_extern_items ()) + item->accept_vis (*this); +} + +void +PatternChecker::visit (LiteralPattern &) +{} + +void +PatternChecker::visit (IdentifierPattern &) +{} + +void +PatternChecker::visit (WildcardPattern &) +{} + +void +PatternChecker::visit (RangePatternBoundLiteral &) +{} + +void +PatternChecker::visit (RangePatternBoundPath &) +{} + +void +PatternChecker::visit (RangePatternBoundQualPath &) +{} + +void +PatternChecker::visit (RangePattern &) +{} + +void +PatternChecker::visit (ReferencePattern &) +{} + +void +PatternChecker::visit (StructPatternFieldTuplePat &) +{} + +void +PatternChecker::visit (StructPatternFieldIdentPat &) +{} + +void +PatternChecker::visit (StructPatternFieldIdent &) +{} + +void +PatternChecker::visit (StructPattern &) +{} + +void +PatternChecker::visit (TupleStructItemsNoRange &) +{} + +void +PatternChecker::visit (TupleStructItemsRange &) +{} + +void +PatternChecker::visit (TupleStructPattern &) +{} + +void +PatternChecker::visit (TuplePatternItemsMultiple &) +{} + +void +PatternChecker::visit (TuplePatternItemsRanged &) +{} + +void +PatternChecker::visit (TuplePattern &) +{} + +void +PatternChecker::visit (SlicePattern &) +{} + +void +PatternChecker::visit (AltPattern &) +{} + +void +PatternChecker::visit (EmptyStmt &) +{} + +void +PatternChecker::visit (LetStmt &stmt) +{ + if (stmt.has_init_expr ()) + stmt.get_init_expr ().accept_vis (*this); +} + +void +PatternChecker::visit (ExprStmt &stmt) +{ + stmt.get_expr ().accept_vis (*this); +} + +void +PatternChecker::visit (TraitBound &) +{} + +void +PatternChecker::visit (ImplTraitType &) +{} + +void +PatternChecker::visit (TraitObjectType &) +{} + +void +PatternChecker::visit (ParenthesisedType &) +{} + +void +PatternChecker::visit (ImplTraitTypeOneBound &) +{} + +void +PatternChecker::visit (TupleType &) +{} + +void +PatternChecker::visit (NeverType &) +{} + +void +PatternChecker::visit (RawPointerType &) +{} + +void +PatternChecker::visit (ReferenceType &) +{} + +void +PatternChecker::visit (ArrayType &) +{} + +void +PatternChecker::visit (SliceType &) +{} + +void +PatternChecker::visit (InferredType &) +{} + +void +PatternChecker::visit (BareFunctionType &) +{} + +bool +Constructor::is_covered_by (const Constructor &o) const +{ + if (o.kind == ConstructorKind::WILDCARD) + return true; + + switch (kind) + { + case ConstructorKind::VARIANT: { + rust_assert (kind == ConstructorKind::VARIANT); + return variant_idx == o.variant_idx; + } + break; + case ConstructorKind::INT_RANGE: { + rust_assert (kind == ConstructorKind::INT_RANGE); + return int_range.lo >= o.int_range.lo && int_range.hi <= o.int_range.hi; + } + break; + case ConstructorKind::WILDCARD: { + // TODO: wildcard is covered by a variant of enum with a single + // variant + return false; + } + break; + case ConstructorKind::STRUCT: { + // Struct pattern is always covered by a other struct constructor. + return true; + } + break; + // TODO: support references + case ConstructorKind::REFERENCE: + default: + rust_unreachable (); + } +} + +bool +Constructor::operator< (const Constructor &o) const +{ + if (kind != o.kind) + return kind < o.kind; + + switch (kind) + { + case ConstructorKind::VARIANT: + return variant_idx < o.variant_idx; + case ConstructorKind::INT_RANGE: + return int_range.lo < o.int_range.lo + || (int_range.lo == o.int_range.lo + && int_range.hi < o.int_range.hi); + case ConstructorKind::STRUCT: + case ConstructorKind::WILDCARD: + case ConstructorKind::REFERENCE: + return false; + default: + rust_unreachable (); + } +} + +std::string +Constructor::to_string () const +{ + switch (kind) + { + case ConstructorKind::STRUCT: + return "STRUCT"; + case ConstructorKind::VARIANT: + return "VARIANT(" + std::to_string (variant_idx) + ")"; + case ConstructorKind::INT_RANGE: + return "RANGE" + std::to_string (int_range.lo) + ".." + + std::to_string (int_range.hi); + case ConstructorKind::WILDCARD: + return "_"; + case ConstructorKind::REFERENCE: + return "REF"; + default: + rust_unreachable (); + } +} + +std::vector<DeconstructedPat> +DeconstructedPat::specialize (const Constructor &other_ctor, + int other_ctor_arity) const +{ + rust_assert (other_ctor.is_covered_by (ctor)); + if (ctor.is_wildcard ()) + return std::vector<DeconstructedPat> ( + other_ctor_arity, + DeconstructedPat (Constructor::make_wildcard (), locus)); + + return fields; +} + +std::string +DeconstructedPat::to_string () const +{ + std::string s = ctor.to_string () + "["; + for (auto &f : fields) + s += f.to_string () + ", "; + + s += "](arity=" + std::to_string (arity) + ")"; + return s; +} + +bool +PatOrWild::is_covered_by (const Constructor &c) const +{ + if (pat.has_value ()) + return pat.value ().get_ctor ().is_covered_by (c); + else + return true; +} + +std::vector<PatOrWild> +PatOrWild::specialize (const Constructor &other_ctor, + int other_ctor_arity) const +{ + if (pat.has_value ()) + { + auto v = pat.value ().specialize (other_ctor, other_ctor_arity); + std::vector<PatOrWild> ret; + for (auto &pat : v) + ret.push_back (PatOrWild::make_pattern (pat)); + + return ret; + } + else + { + return std::vector<PatOrWild> (other_ctor_arity, + PatOrWild::make_wildcard ()); + } +} + +std::string +PatOrWild::to_string () const +{ + if (pat.has_value ()) + return pat.value ().to_string (); + else + return "Wild"; +} + +void +PatStack::pop_head_constructor (const Constructor &other_ctor, + int other_ctor_arity) +{ + rust_assert (!pats.empty ()); + rust_assert (other_ctor.is_covered_by (head ().ctor ())); + + PatOrWild &hd = head (); + auto v = hd.specialize (other_ctor, other_ctor_arity); + { + std::string s = "["; + for (auto &pat : v) + s += pat.to_string () + ", "; + s += "]"; + + rust_debug ("specialize %s with %s to %s", hd.to_string ().c_str (), + other_ctor.to_string ().c_str (), s.c_str ()); + } + pop_head (); + for (auto &pat : v) + pats.push_back (pat); +} + +std::string +MatrixRow::to_string () const +{ + std::string s; + for (const PatOrWild &pat : pats.get_subpatterns ()) + s += pat.to_string () + ", "; + return s; +} + +std::vector<PlaceInfo> +PlaceInfo::specialize (const Constructor &c) const +{ + switch (c.get_kind ()) + { + case Constructor::ConstructorKind::WILDCARD: + case Constructor::ConstructorKind::INT_RANGE: { + return {}; + } + break; + case Constructor::ConstructorKind::STRUCT: + case Constructor::ConstructorKind::VARIANT: { + rust_assert (ty->get_kind () == TyTy::TypeKind::ADT); + TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (ty); + switch (adt->get_adt_kind ()) + { + case TyTy::ADTType::ADTKind::ENUM: + case TyTy::ADTType::ADTKind::STRUCT_STRUCT: + case TyTy::ADTType::ADTKind::TUPLE_STRUCT: { + TyTy::VariantDef *variant + = adt->get_variants ().at (c.get_variant_index ()); + if (variant->get_variant_type () + == TyTy::VariantDef::VariantType::NUM) + return {}; + + std::vector<PlaceInfo> new_place_infos; + for (auto &field : variant->get_fields ()) + new_place_infos.push_back (field->get_field_type ()); + + return new_place_infos; + } + break; + case TyTy::ADTType::ADTKind::UNION: { + // TODO: support unions + rust_unreachable (); + } + } + } + break; + default: { + rust_unreachable (); + } + break; + } + + rust_unreachable (); +} + +Matrix +Matrix::specialize (const Constructor &ctor) const +{ + auto subfields_place_info = place_infos.at (0).specialize (ctor); + + std::vector<MatrixRow> new_rows; + for (const MatrixRow &row : rows) + { + PatStack pats = row.get_pats_clone (); + const PatOrWild &hd = pats.head (); + if (ctor.is_covered_by (hd.ctor ())) + { + pats.pop_head_constructor (ctor, subfields_place_info.size ()); + new_rows.push_back (MatrixRow (pats, row.is_under_guard ())); + } + } + + if (place_infos.empty ()) + return Matrix (new_rows, {}); + + // push subfields of the first fields after specialization + std::vector<PlaceInfo> new_place_infos = subfields_place_info; + // add place infos for the rest of the fields + for (size_t i = 1; i < place_infos.size (); i++) + new_place_infos.push_back (place_infos.at (i)); + + return Matrix (new_rows, new_place_infos); +} + +std::string +Matrix::to_string () const +{ + std::string s = "[\n"; + for (const MatrixRow &row : rows) + s += "row: " + row.to_string () + "\n"; + + s += "](place_infos=["; + for (const PlaceInfo &place_info : place_infos) + s += place_info.get_type ()->as_string () + ", "; + + s += "])"; + return s; +} + +std::string +WitnessPat::to_string () const +{ + switch (ctor.get_kind ()) + { + case Constructor::ConstructorKind::STRUCT: { + TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (ty); + TyTy::VariantDef *variant + = adt->get_variants ().at (ctor.get_variant_index ()); + std::string buf; + buf += adt->get_identifier (); + + buf += " {"; + if (!fields.empty ()) + buf += " "; + + for (size_t i = 0; i < fields.size (); i++) + { + buf += variant->get_fields ().at (i)->get_name () + ": "; + buf += fields.at (i).to_string (); + if (i < fields.size () - 1) + buf += ", "; + } + if (!fields.empty ()) + buf += " "; + + buf += "}"; + return buf; + } + break; + case Constructor::ConstructorKind::VARIANT: { + std::string buf; + TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (ty); + buf += adt->get_identifier (); + TyTy::VariantDef *variant + = adt->get_variants ().at (ctor.get_variant_index ()); + buf += "::" + variant->get_identifier (); + + switch (variant->get_variant_type ()) + { + case TyTy::VariantDef::VariantType::NUM: { + return buf; + } + break; + case TyTy::VariantDef::VariantType::TUPLE: { + buf += "("; + for (size_t i = 0; i < fields.size (); i++) + { + buf += fields.at (i).to_string (); + if (i < fields.size () - 1) + buf += ", "; + } + buf += ")"; + return buf; + } + break; + case TyTy::VariantDef::VariantType::STRUCT: { + buf += " {"; + if (!fields.empty ()) + buf += " "; + + for (size_t i = 0; i < fields.size (); i++) + { + buf += variant->get_fields ().at (i)->get_name () + ": "; + buf += fields.at (i).to_string (); + if (i < fields.size () - 1) + buf += ", "; + } + + if (!fields.empty ()) + buf += " "; + + buf += "}"; + } + break; + default: { + rust_unreachable (); + } + break; + } + return buf; + } + break; + case Constructor::ConstructorKind::INT_RANGE: { + // TODO: implement + rust_unreachable (); + } + break; + case Constructor::ConstructorKind::WILDCARD: { + return "_"; + } + break; + case Constructor::ConstructorKind::REFERENCE: { + // TODO: implement + rust_unreachable (); + } + break; + default: { + rust_unreachable (); + } + break; + } + rust_unreachable (); +} + +void +WitnessMatrix::apply_constructor (const Constructor &ctor, + const std::set<Constructor> &missings, + TyTy::BaseType *ty) +{ + int arity = 0; + // TODO: only support struct and variant ctor for now. + switch (ctor.get_kind ()) + { + case Constructor::ConstructorKind::WILDCARD: { + arity = 0; + } + break; + case Constructor::ConstructorKind::STRUCT: + case Constructor::ConstructorKind::VARIANT: { + if (ty->get_kind () == TyTy::TypeKind::ADT) + { + TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (ty); + TyTy::VariantDef *variant + = adt->get_variants ().at (ctor.get_variant_index ()); + if (variant->get_variant_type () == TyTy::VariantDef::NUM) + arity = 0; + else + arity = variant->get_fields ().size (); + } + } + break; + default: { + rust_unreachable (); + } + } + + std::string buf; + for (auto &stack : patstacks) + { + buf += "["; + for (auto &pat : stack) + buf += pat.to_string () + ", "; + + buf += "]\n"; + } + rust_debug ("witness pats:\n%s", buf.c_str ()); + + for (auto &stack : patstacks) + { + std::vector<WitnessPat> subfield; + for (int i = 0; i < arity; i++) + { + if (stack.empty ()) + subfield.push_back (WitnessPat::make_wildcard (ty)); + else + { + subfield.push_back (stack.back ()); + stack.pop_back (); + } + } + + stack.push_back (WitnessPat (ctor, subfield, ty)); + } +} + +void +WitnessMatrix::extend (const WitnessMatrix &other) +{ + patstacks.insert (patstacks.end (), other.patstacks.begin (), + other.patstacks.end ()); +} + +// forward declarations +static DeconstructedPat +lower_pattern (Resolver::TypeCheckContext *ctx, HIR::Pattern &pattern, + TyTy::BaseType *scrutinee_ty); + +static DeconstructedPat +lower_tuple_pattern (Resolver::TypeCheckContext *ctx, + HIR::TupleStructPattern &pattern, + TyTy::VariantDef *variant, Constructor &ctor) +{ + int arity = variant->get_fields ().size (); + HIR::TupleStructItems &elems = pattern.get_items (); + + std::vector<DeconstructedPat> fields; + switch (elems.get_item_type ()) + { + case HIR::TupleStructItems::ItemType::MULTIPLE: { + HIR::TupleStructItemsNoRange &multiple + = static_cast<HIR::TupleStructItemsNoRange &> (elems); + + rust_assert (variant->get_fields ().size () + == multiple.get_patterns ().size ()); + + for (size_t i = 0; i < multiple.get_patterns ().size (); i++) + { + fields.push_back ( + lower_pattern (ctx, *multiple.get_patterns ().at (i), + variant->get_fields ().at (i)->get_field_type ())); + } + return DeconstructedPat (ctor, arity, fields, pattern.get_locus ()); + } + break; + case HIR::TupleStructItems::ItemType::RANGED: { + // TODO: ranged tuple struct items + rust_unreachable (); + } + break; + default: { + rust_unreachable (); + } + } +} + +static DeconstructedPat +lower_struct_pattern (Resolver::TypeCheckContext *ctx, + HIR::StructPattern &pattern, TyTy::VariantDef *variant, + Constructor ctor) +{ + int arity = variant->get_fields ().size (); + + // Initialize all field patterns to wildcard. + std::vector<DeconstructedPat> fields + = std::vector<DeconstructedPat> (arity, DeconstructedPat::make_wildcard ( + pattern.get_locus ())); + + std::map<std::string, int> field_map; + for (int i = 0; i < arity; i++) + { + auto &f = variant->get_fields ().at (i); + field_map[f->get_name ()] = i; + } + + // Fill in the fields with the present patterns. + HIR::StructPatternElements elems = pattern.get_struct_pattern_elems (); + for (auto &elem : elems.get_struct_pattern_fields ()) + { + switch (elem->get_item_type ()) + { + case HIR::StructPatternField::ItemType::IDENT: { + HIR::StructPatternFieldIdent *ident + = static_cast<HIR::StructPatternFieldIdent *> (elem.get ()); + int field_idx + = field_map.at (ident->get_identifier ().as_string ()); + fields.at (field_idx) + = DeconstructedPat::make_wildcard (pattern.get_locus ()); + } + break; + case HIR::StructPatternField::ItemType::IDENT_PAT: { + HIR::StructPatternFieldIdentPat *ident_pat + = static_cast<HIR::StructPatternFieldIdentPat *> (elem.get ()); + int field_idx + = field_map.at (ident_pat->get_identifier ().as_string ()); + fields.at (field_idx) = lower_pattern ( + ctx, ident_pat->get_pattern (), + variant->get_fields ().at (field_idx)->get_field_type ()); + } + break; + case HIR::StructPatternField::ItemType::TUPLE_PAT: { + // TODO: tuple: pat + rust_unreachable (); + } + break; + default: { + rust_unreachable (); + } + } + } + + return DeconstructedPat{ctor, arity, fields, pattern.get_locus ()}; +}; + +static DeconstructedPat +lower_pattern (Resolver::TypeCheckContext *ctx, HIR::Pattern &pattern, + TyTy::BaseType *scrutinee_ty) +{ + HIR::Pattern::PatternType pat_type = pattern.get_pattern_type (); + switch (pat_type) + { + case HIR::Pattern::PatternType::WILDCARD: + case HIR::Pattern::PatternType::IDENTIFIER: { + return DeconstructedPat::make_wildcard (pattern.get_locus ()); + } + break; + case HIR::Pattern::PatternType::PATH: { + // TODO: support constants, associated constants, enum variants and + // structs + // https://doc.rust-lang.org/reference/patterns.html#path-patterns + // unimplemented. Treat this pattern as wildcard for now. + return DeconstructedPat::make_wildcard (pattern.get_locus ()); + } + break; + case HIR::Pattern::PatternType::REFERENCE: { + // TODO: unimplemented. Treat this pattern as wildcard for now. + return DeconstructedPat::make_wildcard (pattern.get_locus ()); + } + break; + case HIR::Pattern::PatternType::STRUCT: + case HIR::Pattern::PatternType::TUPLE_STRUCT: { + HirId path_id = UNKNOWN_HIRID; + if (pat_type == HIR::Pattern::PatternType::STRUCT) + { + HIR::StructPattern &struct_pattern + = static_cast<HIR::StructPattern &> (pattern); + path_id = struct_pattern.get_path ().get_mappings ().get_hirid (); + } + else + { + HIR::TupleStructPattern &tuple_pattern + = static_cast<HIR::TupleStructPattern &> (pattern); + path_id = tuple_pattern.get_path ().get_mappings ().get_hirid (); + } + + rust_assert (scrutinee_ty->get_kind () == TyTy::TypeKind::ADT); + TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (scrutinee_ty); + + Constructor ctor = Constructor::make_struct (); + TyTy::VariantDef *variant; + if (adt->is_struct_struct () || adt->is_tuple_struct ()) + variant = adt->get_variants ().at (0); + else if (adt->is_enum ()) + { + HirId variant_id = UNKNOWN_HIRID; + bool ok = ctx->lookup_variant_definition (path_id, &variant_id); + rust_assert (ok); + + int variant_idx; + ok = adt->lookup_variant_by_id (variant_id, &variant, &variant_idx); + rust_assert (ok); + + ctor = Constructor::make_variant (variant_idx); + } + else + { + rust_unreachable (); + } + rust_assert (variant->get_variant_type () + == TyTy::VariantDef::VariantType::TUPLE + || variant->get_variant_type () + == TyTy::VariantDef::VariantType::STRUCT); + + if (pat_type == HIR::Pattern::PatternType::STRUCT) + { + HIR::StructPattern &struct_pattern + = static_cast<HIR::StructPattern &> (pattern); + return lower_struct_pattern (ctx, struct_pattern, variant, ctor); + } + else + { + HIR::TupleStructPattern &tuple_pattern + = static_cast<HIR::TupleStructPattern &> (pattern); + return lower_tuple_pattern (ctx, tuple_pattern, variant, ctor); + } + } + break; + case HIR::Pattern::PatternType::TUPLE: { + // TODO: unimplemented. Treat this pattern as wildcard for now. + return DeconstructedPat::make_wildcard (pattern.get_locus ()); + } + break; + case HIR::Pattern::PatternType::SLICE: { + // TODO: unimplemented. Treat this pattern as wildcard for now. + return DeconstructedPat::make_wildcard (pattern.get_locus ()); + } + break; + case HIR::Pattern::PatternType::ALT: { + // TODO: unimplemented. Treat this pattern as wildcard for now. + return DeconstructedPat::make_wildcard (pattern.get_locus ()); + } + break; + case HIR::Pattern::PatternType::LITERAL: { + // TODO: unimplemented. Treat this pattern as wildcard for now. + return DeconstructedPat::make_wildcard (pattern.get_locus ()); + } + break; + case HIR::Pattern::PatternType::RANGE: { + // TODO: unimplemented. Treat this pattern as wildcard for now. + return DeconstructedPat::make_wildcard (pattern.get_locus ()); + } + break; + case HIR::Pattern::PatternType::GROUPED: { + // TODO: unimplemented. Treat this pattern as wildcard for now. + return DeconstructedPat::make_wildcard (pattern.get_locus ()); + } + break; + default: { + rust_unreachable (); + } + } +} + +static MatchArm +lower_arm (Resolver::TypeCheckContext *ctx, HIR::MatchCase &arm, + TyTy::BaseType *scrutinee_ty) +{ + rust_assert (arm.get_arm ().get_patterns ().size () > 0); + + DeconstructedPat pat + = lower_pattern (ctx, *arm.get_arm ().get_patterns ().at (0), scrutinee_ty); + return MatchArm (pat, arm.get_arm ().has_match_arm_guard ()); +} + +std::pair<std::set<Constructor>, std::set<Constructor>> +split_constructors (std::vector<Constructor> &ctors, PlaceInfo &place_info) +{ + bool all_wildcard = true; + for (auto &ctor : ctors) + { + if (!ctor.is_wildcard ()) + all_wildcard = false; + } + + // first pass for the case that all patterns are wildcard + if (all_wildcard) + return std::make_pair (std::set<Constructor> ( + {Constructor::make_wildcard ()}), + std::set<Constructor> ()); + + // TODO: only support enums and structs for now. + TyTy::BaseType *ty = place_info.get_type (); + rust_assert (ty->get_kind () == TyTy::TypeKind::ADT); + TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (ty); + rust_assert (adt->is_enum () || adt->is_struct_struct () + || adt->is_tuple_struct ()); + + std::set<Constructor> universe; + if (adt->is_enum ()) + { + for (size_t i = 0; i < adt->get_variants ().size (); i++) + universe.insert (Constructor::make_variant (i)); + } + else if (adt->is_struct_struct () || adt->is_tuple_struct ()) + { + universe.insert (Constructor::make_struct ()); + } + + std::set<Constructor> present; + for (auto &ctor : ctors) + { + if (ctor.is_wildcard ()) + return std::make_pair (universe, std::set<Constructor> ()); + else + present.insert (ctor); + } + + std::set<Constructor> missing; + std::set_difference (universe.begin (), universe.end (), present.begin (), + present.end (), std::inserter (missing, missing.end ())); + return std::make_pair (universe, missing); +} + +// The core of the algorithm. It computes the usefulness and exhaustiveness of a +// given matrix recursively. +// TODO: calculate usefulness +static WitnessMatrix +compute_exhaustiveness_and_usefulness (Resolver::TypeCheckContext *ctx, + Matrix &matrix) +{ + rust_debug ("call compute_exhaustiveness_and_usefulness"); + rust_debug ("matrix: %s", matrix.to_string ().c_str ()); + + if (matrix.get_rows ().empty ()) + { + // no rows left. This means a non-exhaustive pattern. + rust_debug ("non-exhaustive subpattern found"); + return WitnessMatrix::make_unit (); + } + + // Base case: there are no columns in matrix. + if (matrix.get_place_infos ().empty ()) + return WitnessMatrix::make_empty (); + + std::vector<Constructor> heads; + for (auto head : matrix.heads ()) + heads.push_back (head.ctor ()); + + // TODO: not sure missing ctors need to be calculated + auto ctors_and_missings + = split_constructors (heads, matrix.get_place_infos ().at (0)); + std::set<Constructor> ctors = ctors_and_missings.first; + std::set<Constructor> missings = ctors_and_missings.second; + + WitnessMatrix ret = WitnessMatrix::make_empty (); + for (auto &ctor : ctors) + { + rust_debug ("specialize with %s", ctor.to_string ().c_str ()); + // TODO: Instead of creating new matrix, we can change the original matrix + // and use it for sub-pattern matching. It will significantly reduce + // memory usage. + Matrix spec_matrix = matrix.specialize (ctor); + + WitnessMatrix witness + = compute_exhaustiveness_and_usefulness (ctx, spec_matrix); + + TyTy::BaseType *ty = matrix.get_place_infos ().at (0).get_type (); + witness.apply_constructor (ctor, missings, ty); + ret.extend (witness); + } + + return ret; +} + +static void +emit_exhaustiveness_error (Resolver::TypeCheckContext *ctx, + HIR::MatchExpr &expr, WitnessMatrix &witness) +{ + TyTy::BaseType *scrutinee_ty; + bool ok + = ctx->lookup_type (expr.get_scrutinee_expr ().get_mappings ().get_hirid (), + &scrutinee_ty); + rust_assert (ok); + + if (!witness.empty ()) + { + std::stringstream buf; + for (size_t i = 0; i < witness.get_stacks ().size (); i++) + { + auto &stack = witness.get_stacks ().at (i); + WitnessPat w = WitnessPat::make_wildcard (scrutinee_ty); + if (!stack.empty ()) + w = stack.at (0); + + rust_debug ("Witness[%d]: %s", (int) i, w.to_string ().c_str ()); + buf << "'" << w.to_string () << "'"; + if (i != witness.get_stacks ().size () - 1) + buf << " and "; + } + rust_error_at (expr.get_scrutinee_expr ().get_locus (), + "non-exhaustive patterns: %s not covered", + buf.str ().c_str ()); + } + else + { + rust_debug ("no witness found"); + } +} + +// Entry point for computing match usefulness and check exhaustiveness +void +check_match_usefulness (Resolver::TypeCheckContext *ctx, + TyTy::BaseType *scrutinee_ty, HIR::MatchExpr &expr) +{ + if (!expr.has_match_arms ()) + return; + + // Lower the arms to a more convenient representation. + std::vector<MatrixRow> rows; + for (auto &arm : expr.get_match_cases ()) + { + PatStack pats; + MatchArm lowered = lower_arm (ctx, arm, scrutinee_ty); + PatOrWild pat = PatOrWild::make_pattern (lowered.get_pat ()); + pats.push (pat); + rows.push_back (MatrixRow (pats, lowered.has_guard ())); + } + + std::vector<PlaceInfo> place_infos = {{PlaceInfo (scrutinee_ty)}}; + Matrix matrix{rows, place_infos}; + + WitnessMatrix witness = compute_exhaustiveness_and_usefulness (ctx, matrix); + + emit_exhaustiveness_error (ctx, expr, witness); +} + +} // namespace Analysis +} // namespace Rust diff --git a/gcc/rust/checks/errors/rust-hir-pattern-analysis.h b/gcc/rust/checks/errors/rust-hir-pattern-analysis.h new file mode 100644 index 0000000..9c43d41 --- /dev/null +++ b/gcc/rust/checks/errors/rust-hir-pattern-analysis.h @@ -0,0 +1,524 @@ +// Copyright (C) 2020-2024 Free Software Foundation, Inc. + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +#ifndef RUST_HIR_PATTERN_ANALYSIS_H +#define RUST_HIR_PATTERN_ANALYSIS_H + +#include "rust-system.h" +#include "rust-hir-expr.h" +#include "rust-hir-type-check.h" +#include "rust-system.h" +#include "rust-tyty.h" +#include "optional.h" +#include "rust-hir-visitor.h" +#include "rust-name-resolver.h" + +namespace Rust { +namespace Analysis { + +using namespace HIR; + +void +check_match_usefulness (Resolver::TypeCheckContext *ctx, + TyTy::BaseType *scrutinee_ty, HIR::MatchExpr &expr); + +class PatternChecker : public HIR::HIRFullVisitor +{ +public: + PatternChecker (); + + void go (HIR::Crate &crate); + +private: + Resolver::TypeCheckContext &tyctx; + Resolver::Resolver &resolver; + Analysis::Mappings &mappings; + + virtual void visit (Lifetime &lifetime) override; + virtual void visit (LifetimeParam &lifetime_param) override; + virtual void visit (PathInExpression &path) override; + virtual void visit (TypePathSegment &segment) override; + virtual void visit (TypePathSegmentGeneric &segment) override; + virtual void visit (TypePathSegmentFunction &segment) override; + virtual void visit (TypePath &path) override; + virtual void visit (QualifiedPathInExpression &path) override; + virtual void visit (QualifiedPathInType &path) override; + virtual void visit (LiteralExpr &expr) override; + virtual void visit (BorrowExpr &expr) override; + virtual void visit (DereferenceExpr &expr) override; + virtual void visit (ErrorPropagationExpr &expr) override; + virtual void visit (NegationExpr &expr) override; + virtual void visit (ArithmeticOrLogicalExpr &expr) override; + virtual void visit (ComparisonExpr &expr) override; + virtual void visit (LazyBooleanExpr &expr) override; + virtual void visit (TypeCastExpr &expr) override; + virtual void visit (AssignmentExpr &expr) override; + virtual void visit (CompoundAssignmentExpr &expr) override; + virtual void visit (GroupedExpr &expr) override; + virtual void visit (ArrayElemsValues &elems) override; + virtual void visit (ArrayElemsCopied &elems) override; + virtual void visit (ArrayExpr &expr) override; + virtual void visit (ArrayIndexExpr &expr) override; + virtual void visit (TupleExpr &expr) override; + virtual void visit (TupleIndexExpr &expr) override; + virtual void visit (StructExprStruct &expr) override; + virtual void visit (StructExprFieldIdentifier &field) override; + virtual void visit (StructExprFieldIdentifierValue &field) override; + virtual void visit (StructExprFieldIndexValue &field) override; + virtual void visit (StructExprStructFields &expr) override; + virtual void visit (StructExprStructBase &expr) override; + virtual void visit (CallExpr &expr) override; + virtual void visit (MethodCallExpr &expr) override; + virtual void visit (FieldAccessExpr &expr) override; + virtual void visit (BlockExpr &expr) override; + virtual void visit (ClosureExpr &expr) override; + virtual void visit (ContinueExpr &expr) override; + virtual void visit (BreakExpr &expr) override; + virtual void visit (RangeFromToExpr &expr) override; + virtual void visit (RangeFromExpr &expr) override; + virtual void visit (RangeToExpr &expr) override; + virtual void visit (RangeFullExpr &expr) override; + virtual void visit (RangeFromToInclExpr &expr) override; + virtual void visit (RangeToInclExpr &expr) override; + virtual void visit (ReturnExpr &expr) override; + virtual void visit (UnsafeBlockExpr &expr) override; + virtual void visit (LoopExpr &expr) override; + virtual void visit (WhileLoopExpr &expr) override; + virtual void visit (WhileLetLoopExpr &expr) override; + virtual void visit (IfExpr &expr) override; + virtual void visit (IfExprConseqElse &expr) override; + virtual void visit (HIR::MatchExpr &expr) override; + virtual void visit (AwaitExpr &expr) override; + virtual void visit (AsyncBlockExpr &expr) override; + virtual void visit (InlineAsm &expr) override; + virtual void visit (TypeParam ¶m) override; + virtual void visit (ConstGenericParam ¶m) override; + virtual void visit (LifetimeWhereClauseItem &item) override; + virtual void visit (TypeBoundWhereClauseItem &item) override; + virtual void visit (Module &module) override; + virtual void visit (ExternCrate &crate) override; + virtual void visit (UseTreeGlob &use_tree) override; + virtual void visit (UseTreeList &use_tree) override; + virtual void visit (UseTreeRebind &use_tree) override; + virtual void visit (UseDeclaration &use_decl) override; + virtual void visit (Function &function) override; + virtual void visit (TypeAlias &type_alias) override; + virtual void visit (StructStruct &struct_item) override; + virtual void visit (TupleStruct &tuple_struct) override; + virtual void visit (EnumItem &item) override; + virtual void visit (EnumItemTuple &item) override; + virtual void visit (EnumItemStruct &item) override; + virtual void visit (EnumItemDiscriminant &item) override; + virtual void visit (Enum &enum_item) override; + virtual void visit (Union &union_item) override; + virtual void visit (ConstantItem &const_item) override; + virtual void visit (StaticItem &static_item) override; + virtual void visit (TraitItemFunc &item) override; + virtual void visit (TraitItemConst &item) override; + virtual void visit (TraitItemType &item) override; + virtual void visit (Trait &trait) override; + virtual void visit (ImplBlock &impl) override; + virtual void visit (ExternalStaticItem &item) override; + virtual void visit (ExternalFunctionItem &item) override; + virtual void visit (ExternalTypeItem &item) override; + virtual void visit (ExternBlock &block) override; + virtual void visit (LiteralPattern &pattern) override; + virtual void visit (IdentifierPattern &pattern) override; + virtual void visit (WildcardPattern &pattern) override; + virtual void visit (RangePatternBoundLiteral &bound) override; + virtual void visit (RangePatternBoundPath &bound) override; + virtual void visit (RangePatternBoundQualPath &bound) override; + virtual void visit (RangePattern &pattern) override; + virtual void visit (ReferencePattern &pattern) override; + virtual void visit (StructPatternFieldTuplePat &field) override; + virtual void visit (StructPatternFieldIdentPat &field) override; + virtual void visit (StructPatternFieldIdent &field) override; + virtual void visit (StructPattern &pattern) override; + virtual void visit (TupleStructItemsNoRange &tuple_items) override; + virtual void visit (TupleStructItemsRange &tuple_items) override; + virtual void visit (TupleStructPattern &pattern) override; + virtual void visit (TuplePatternItemsMultiple &tuple_items) override; + virtual void visit (TuplePatternItemsRanged &tuple_items) override; + virtual void visit (TuplePattern &pattern) override; + virtual void visit (SlicePattern &pattern) override; + virtual void visit (AltPattern &pattern) override; + virtual void visit (EmptyStmt &stmt) override; + virtual void visit (LetStmt &stmt) override; + virtual void visit (ExprStmt &stmt) override; + virtual void visit (TraitBound &bound) override; + virtual void visit (ImplTraitType &type) override; + virtual void visit (TraitObjectType &type) override; + virtual void visit (ParenthesisedType &type) override; + virtual void visit (ImplTraitTypeOneBound &type) override; + virtual void visit (TupleType &type) override; + virtual void visit (NeverType &type) override; + virtual void visit (RawPointerType &type) override; + virtual void visit (ReferenceType &type) override; + virtual void visit (ArrayType &type) override; + virtual void visit (SliceType &type) override; + virtual void visit (InferredType &type) override; + virtual void visit (BareFunctionType &type) override; +}; + +struct IntRange +{ + int64_t lo; + int64_t hi; +}; + +class Constructor +{ +public: + enum class ConstructorKind + { + // tuple or struct + STRUCT, + // enum variant + VARIANT, + // integers + INT_RANGE, + // user-provided wildcard + WILDCARD, + // references + REFERENCE, + }; + + static Constructor make_wildcard () + { + return Constructor (ConstructorKind::WILDCARD); + } + + static Constructor make_reference () + { + return Constructor (ConstructorKind::REFERENCE); + } + + static Constructor make_struct () + { + Constructor c (ConstructorKind::STRUCT); + c.variant_idx = 0; + return c; + } + + static Constructor make_variant (int variant_idx) + { + Constructor c (ConstructorKind::VARIANT); + c.variant_idx = variant_idx; + return c; + } + + ConstructorKind get_kind () const { return kind; } + + int get_variant_index () const + { + rust_assert (kind == ConstructorKind::VARIANT + || kind == ConstructorKind::STRUCT); + return variant_idx; + } + + bool is_covered_by (const Constructor &o) const; + + bool is_wildcard () const { return kind == ConstructorKind::WILDCARD; } + + // Requrired by std::set<T> + bool operator< (const Constructor &o) const; + + std::string to_string () const; + +private: + Constructor (ConstructorKind kind) : kind (kind), variant_idx (0) {} + ConstructorKind kind; + + union + { + // for enum variants, the variant index (always 0 for structs) + int variant_idx; + + // for integer ranges, the range + IntRange int_range; + }; +}; + +class DeconstructedPat +{ +public: + DeconstructedPat (Constructor ctor, int arity, + std::vector<DeconstructedPat> fields, location_t locus) + : ctor (ctor), arity (arity), fields (fields) + {} + + static DeconstructedPat make_wildcard (location_t locus) + { + return DeconstructedPat (Constructor::make_wildcard (), locus); + } + + static DeconstructedPat make_reference (location_t locus) + { + return DeconstructedPat (Constructor::make_reference (), locus); + } + + const Constructor &get_ctor () const { return ctor; } + + int get_arity () const { return arity; } + + std::vector<DeconstructedPat> specialize (const Constructor &other_ctor, + int other_ctor_arity) const; + + std::string to_string () const; + +private: + DeconstructedPat (Constructor ctor, location_t locus) + : ctor (ctor), arity (0), locus (locus) + {} + + Constructor ctor; + int arity; + std::vector<DeconstructedPat> fields; + location_t locus; +}; + +class PatOrWild +{ +public: + static PatOrWild make_pattern (DeconstructedPat pat) + { + return PatOrWild (pat); + } + + static PatOrWild make_wildcard () { return PatOrWild ({}); } + + bool is_wildcard () const + { + return !(pat.has_value () && !pat.value ().get_ctor ().is_wildcard ()); + } + + bool is_covered_by (const Constructor &c) const; + + // Returns the pattern if it is not a wildcard. + const tl::optional<DeconstructedPat> &get_pat () const + { + rust_assert (pat.has_value ()); + return pat; + } + + Constructor ctor () const + { + if (pat.has_value ()) + return pat.value ().get_ctor (); + else + return Constructor::make_wildcard (); + } + + std::vector<PatOrWild> specialize (const Constructor &other_ctor, + int other_ctor_arity) const; + + std::string to_string () const; + +private: + PatOrWild (tl::optional<DeconstructedPat> pat) : pat (pat) {} + + tl::optional<DeconstructedPat> pat; +}; + +class PatStack +{ +public: + PatStack () : relevant (false) {} + + void push (PatOrWild pat) { pats.push_back (pat); } + + bool empty () const { return pats.empty (); } + + PatOrWild &head () + { + rust_assert (!pats.empty ()); + return pats.front (); + } + + const PatOrWild &head () const + { + rust_assert (!pats.empty ()); + return pats.front (); + } + + // Only called if the head is a constructor which is convered by o. + void pop_head_constructor (const Constructor &other_ctor, + int other_ctor_arity); + + const std::deque<PatOrWild> &get_subpatterns () const { return pats; } + +private: + void pop_head () { pats.pop_front (); } + + std::deque<PatOrWild> pats; + bool relevant; +}; + +class MatrixRow +{ +public: + MatrixRow (PatStack pats, bool is_under_guard_) + : pats (pats), is_under_guard_ (is_under_guard_) + // useful (false), + // head_is_branch (false), + {} + + PatStack &get_pats () { return pats; } + + PatStack get_pats_clone () const { return pats; } + + const PatOrWild &head () const { return pats.head (); } + PatOrWild &head () { return pats.head (); } + + bool is_under_guard () const { return is_under_guard_; } + + std::string to_string () const; + +private: + PatStack pats; + bool is_under_guard_; + // TODO: manage usefulness +}; + +class PlaceInfo +{ +public: + PlaceInfo (TyTy::BaseType *ty) : ty (ty) {} + + TyTy::BaseType *get_type () const { return ty; } + + std::vector<PlaceInfo> specialize (const Constructor &c) const; + +private: + TyTy::BaseType *ty; +}; + +class Matrix +{ +public: + Matrix (std::vector<MatrixRow> rows, std::vector<PlaceInfo> place_infos) + : rows (rows), place_infos (place_infos) + {} + + Matrix () {} + + std::vector<MatrixRow> &get_rows () { return rows; } + + void push_row (const MatrixRow &row) { rows.push_back (row); } + + std::vector<PlaceInfo> &get_place_infos () { return place_infos; } + + std::vector<PatOrWild> heads () const + { + std::vector<PatOrWild> ret; + for (const MatrixRow &row : rows) + ret.push_back (row.head ()); + + return ret; + } + + Matrix specialize (const Constructor &ctor) const; + + std::string to_string () const; + +private: + std::vector<MatrixRow> rows; + std::vector<PlaceInfo> place_infos; +}; + +class MatchArm +{ +public: + MatchArm (DeconstructedPat pat, bool has_guard_) + : pat (pat), has_guard_ (has_guard_) + {} + + DeconstructedPat get_pat () const { return pat; } + + bool has_guard () const { return has_guard_; } + +private: + DeconstructedPat pat; + bool has_guard_; +}; + +class WitnessPat +{ +public: + WitnessPat (Constructor ctor, std::vector<WitnessPat> fields, + TyTy::BaseType *ty) + : ctor (ctor), fields (fields), ty (ty) + {} + + static WitnessPat make_wildcard (TyTy::BaseType *ty) + { + return WitnessPat (Constructor::make_wildcard (), {}, ty); + } + + const Constructor &get_ctor () const { return ctor; } + + const std::vector<WitnessPat> &get_fields () const { return fields; } + + TyTy::BaseType *get_type () const { return ty; } + + std::string to_string () const; + +private: + Constructor ctor; + std::vector<WitnessPat> fields; + TyTy::BaseType *ty; +}; + +class WitnessMatrix +{ +public: + // Create an empty witness matrix. + static WitnessMatrix make_empty () { return WitnessMatrix ({}); } + + // Create a unit witness matrix, a new single witness. + static WitnessMatrix make_unit () + { + return WitnessMatrix ({std::vector<WitnessPat> ()}); + } + + bool empty () const { return patstacks.empty (); } + + const std::vector<std::vector<WitnessPat>> &get_stacks () const + { + return patstacks; + } + + // Reverses specialization. + void apply_constructor (const Constructor &ctor, + const std::set<Constructor> &missings, + TyTy::BaseType *ty); + + void extend (const WitnessMatrix &other); + +private: + WitnessMatrix (std::vector<std::vector<WitnessPat>> patstacks) + : patstacks (patstacks) + {} + + std::vector<std::vector<WitnessPat>> patstacks; +}; + +} // namespace Analysis +} // namespace Rust + +#endif diff --git a/gcc/rust/checks/errors/rust-readonly-check.cc b/gcc/rust/checks/errors/rust-readonly-check.cc index b899898..c128933 100644 --- a/gcc/rust/checks/errors/rust-readonly-check.cc +++ b/gcc/rust/checks/errors/rust-readonly-check.cc @@ -19,10 +19,13 @@ #include "rust-readonly-check.h" #include "rust-tree.h" #include "rust-gcc.h" +#include "print-tree.h" namespace Rust { namespace Analysis { +static std::map<tree, int> assignment_map = {}; + // ported over from c-family/c-warn.cc void readonly_error (location_t loc, tree arg, enum lvalue_use use) @@ -106,37 +109,68 @@ readonly_error (location_t loc, tree arg, enum lvalue_use use) } static void -check_decl (tree *t) +emit_error (tree *t, tree lhs, enum lvalue_use use) { - if (TREE_CODE (*t) == MODIFY_EXPR) + readonly_error (EXPR_LOCATION (*t), lhs, use); + TREE_OPERAND (*t, 0) = error_mark_node; +} + +static void +check_modify_expr (tree *t) +{ + tree lhs = TREE_OPERAND (*t, 0); + if (TREE_CODE (lhs) == ARRAY_REF || TREE_CODE (lhs) == COMPONENT_REF) + lhs = TREE_OPERAND (lhs, 0); + + tree lhs_type = TREE_TYPE (lhs); + if (TYPE_READONLY (lhs_type) || TREE_READONLY (lhs) || TREE_CONSTANT (lhs)) { - tree lhs = TREE_OPERAND (*t, 0); - if (TREE_READONLY (lhs) || TREE_CONSTANT (lhs)) + if (TREE_CODE (lhs) != VAR_DECL) + emit_error (t, lhs, lv_assign); + else if (!DECL_ARTIFICIAL (lhs)) { - readonly_error (EXPR_LOCATION (*t), lhs, lv_assign); - TREE_OPERAND (*t, 0) = error_mark_node; + if (DECL_INITIAL (lhs) != NULL) + emit_error (t, lhs, lv_assign); + else + { + if (assignment_map.find (lhs) == assignment_map.end ()) + { + assignment_map.insert ({lhs, 0}); + } + assignment_map[lhs]++; + + if (assignment_map[lhs] > 1) + emit_error (t, lhs, lv_assign); + } } } } -static tree -readonly_walk_fn (tree *t, int *, void *) +static void +check_decl (tree *t) { switch (TREE_CODE (*t)) { case MODIFY_EXPR: - check_decl (t); + check_modify_expr (t); break; default: break; } +} + +static tree +readonly_walk_fn (tree *t, int *, void *) +{ + check_decl (t); return NULL_TREE; } void ReadonlyCheck::Lint (Compile::Context &ctx) { + assignment_map.clear (); for (auto &fndecl : ctx.get_func_decls ()) { for (tree p = DECL_ARGUMENTS (fndecl); p != NULL_TREE; p = DECL_CHAIN (p)) @@ -148,12 +182,14 @@ ReadonlyCheck::Lint (Compile::Context &ctx) &readonly_walk_fn, &ctx); } + assignment_map.clear (); for (auto &var : ctx.get_var_decls ()) { tree decl = var->get_decl (); check_decl (&decl); } + assignment_map.clear (); for (auto &const_decl : ctx.get_const_decls ()) { check_decl (&const_decl); diff --git a/gcc/rust/checks/errors/rust-unsafe-checker.cc b/gcc/rust/checks/errors/rust-unsafe-checker.cc index c6ed922..fadfd9d 100644 --- a/gcc/rust/checks/errors/rust-unsafe-checker.cc +++ b/gcc/rust/checks/errors/rust-unsafe-checker.cc @@ -23,6 +23,10 @@ #include "rust-hir-item.h" #include "rust-attribute-values.h" #include "rust-system.h" +#include "rust-immutable-name-resolution-context.h" + +// for flag_name_resolution_2_0 +#include "options.h" namespace Rust { namespace HIR { @@ -216,8 +220,23 @@ UnsafeChecker::visit (PathInExpression &path) NodeId ast_node_id = path.get_mappings ().get_nodeid (); NodeId ref_node_id; - if (!resolver.lookup_resolved_name (ast_node_id, &ref_node_id)) - return; + if (flag_name_resolution_2_0) + { + auto &nr_ctx + = Resolver2_0::ImmutableNameResolutionContext::get ().resolver (); + + auto resolved = nr_ctx.lookup (ast_node_id); + + if (!resolved.has_value ()) + return; + + ref_node_id = resolved.value (); + } + else + { + if (!resolver.lookup_resolved_name (ast_node_id, &ref_node_id)) + return; + } if (auto definition_id = mappings.lookup_node_to_hir (ref_node_id)) { @@ -260,14 +279,14 @@ UnsafeChecker::visit (LiteralExpr &) void UnsafeChecker::visit (BorrowExpr &expr) { - expr.get_expr ()->accept_vis (*this); + expr.get_expr ().accept_vis (*this); } void UnsafeChecker::visit (DereferenceExpr &expr) { TyTy::BaseType *to_deref_type; - auto to_deref = expr.get_expr ()->get_mappings ().get_hirid (); + auto to_deref = expr.get_expr ().get_mappings ().get_hirid (); rust_assert (context.lookup_type (to_deref, &to_deref_type)); @@ -280,60 +299,60 @@ UnsafeChecker::visit (DereferenceExpr &expr) void UnsafeChecker::visit (ErrorPropagationExpr &expr) { - expr.get_expr ()->accept_vis (*this); + expr.get_expr ().accept_vis (*this); } void UnsafeChecker::visit (NegationExpr &expr) { - expr.get_expr ()->accept_vis (*this); + expr.get_expr ().accept_vis (*this); } void UnsafeChecker::visit (ArithmeticOrLogicalExpr &expr) { - expr.get_lhs ()->accept_vis (*this); - expr.get_rhs ()->accept_vis (*this); + expr.get_lhs ().accept_vis (*this); + expr.get_rhs ().accept_vis (*this); } void UnsafeChecker::visit (ComparisonExpr &expr) { - expr.get_lhs ()->accept_vis (*this); - expr.get_rhs ()->accept_vis (*this); + expr.get_lhs ().accept_vis (*this); + expr.get_rhs ().accept_vis (*this); } void UnsafeChecker::visit (LazyBooleanExpr &expr) { - expr.get_lhs ()->accept_vis (*this); - expr.get_rhs ()->accept_vis (*this); + expr.get_lhs ().accept_vis (*this); + expr.get_rhs ().accept_vis (*this); } void UnsafeChecker::visit (TypeCastExpr &expr) { - expr.get_expr ()->accept_vis (*this); + expr.get_expr ().accept_vis (*this); } void UnsafeChecker::visit (AssignmentExpr &expr) { - expr.get_lhs ()->accept_vis (*this); - expr.get_rhs ()->accept_vis (*this); + expr.get_lhs ().accept_vis (*this); + expr.get_rhs ().accept_vis (*this); } void UnsafeChecker::visit (CompoundAssignmentExpr &expr) { - expr.get_lhs ()->accept_vis (*this); - expr.get_rhs ()->accept_vis (*this); + expr.get_lhs ().accept_vis (*this); + expr.get_rhs ().accept_vis (*this); } void UnsafeChecker::visit (GroupedExpr &expr) { - expr.get_expr_in_parens ()->accept_vis (*this); + expr.get_expr_in_parens ().accept_vis (*this); } void @@ -346,20 +365,20 @@ UnsafeChecker::visit (ArrayElemsValues &elems) void UnsafeChecker::visit (ArrayElemsCopied &elems) { - elems.get_elem_to_copy ()->accept_vis (*this); + elems.get_elem_to_copy ().accept_vis (*this); } void UnsafeChecker::visit (ArrayExpr &expr) { - expr.get_internal_elements ()->accept_vis (*this); + expr.get_internal_elements ().accept_vis (*this); } void UnsafeChecker::visit (ArrayIndexExpr &expr) { - expr.get_array_expr ()->accept_vis (*this); - expr.get_index_expr ()->accept_vis (*this); + expr.get_array_expr ().accept_vis (*this); + expr.get_index_expr ().accept_vis (*this); } void @@ -372,7 +391,7 @@ UnsafeChecker::visit (TupleExpr &expr) void UnsafeChecker::visit (TupleIndexExpr &expr) { - expr.get_tuple_expr ()->accept_vis (*this); + expr.get_tuple_expr ().accept_vis (*this); } void @@ -386,13 +405,13 @@ UnsafeChecker::visit (StructExprFieldIdentifier &) void UnsafeChecker::visit (StructExprFieldIdentifierValue &field) { - field.get_value ()->accept_vis (*this); + field.get_value ().accept_vis (*this); } void UnsafeChecker::visit (StructExprFieldIndexValue &field) { - field.get_value ()->accept_vis (*this); + field.get_value ().accept_vis (*this); } void @@ -409,17 +428,32 @@ UnsafeChecker::visit (StructExprStructBase &) void UnsafeChecker::visit (CallExpr &expr) { - if (!expr.get_fnexpr ()) + if (!expr.has_fnexpr ()) return; - NodeId ast_node_id = expr.get_fnexpr ()->get_mappings ().get_nodeid (); + NodeId ast_node_id = expr.get_fnexpr ().get_mappings ().get_nodeid (); NodeId ref_node_id; // There are no unsafe types, and functions are defined in the name resolver. // If we can't find the name, then we're dealing with a type and should return // early. - if (!resolver.lookup_resolved_name (ast_node_id, &ref_node_id)) - return; + if (flag_name_resolution_2_0) + { + auto &nr_ctx + = Resolver2_0::ImmutableNameResolutionContext::get ().resolver (); + + auto resolved = nr_ctx.lookup (ast_node_id); + + if (!resolved.has_value ()) + return; + + ref_node_id = resolved.value (); + } + else + { + if (!resolver.lookup_resolved_name (ast_node_id, &ref_node_id)) + return; + } if (auto definition_id = mappings.lookup_node_to_hir (ref_node_id)) { @@ -448,14 +482,14 @@ UnsafeChecker::visit (MethodCallExpr &expr) context.lookup_type (expr.get_method_name ().get_mappings ().get_hirid (), &method_type); - auto fn = *static_cast<TyTy::FnType *> (method_type); + auto &fn = static_cast<TyTy::FnType &> (*method_type); auto method = mappings.lookup_hir_implitem (fn.get_ref ()); if (!unsafe_context.is_in_context () && method) check_unsafe_call (static_cast<Function *> (method->first), expr.get_locus (), "method"); - expr.get_receiver ()->accept_vis (*this); + expr.get_receiver ().accept_vis (*this); for (auto &arg : expr.get_arguments ()) arg->accept_vis (*this); @@ -464,14 +498,14 @@ UnsafeChecker::visit (MethodCallExpr &expr) void UnsafeChecker::visit (FieldAccessExpr &expr) { - expr.get_receiver_expr ()->accept_vis (*this); + expr.get_receiver_expr ().accept_vis (*this); if (unsafe_context.is_in_context ()) return; TyTy::BaseType *receiver_ty; auto ok = context.lookup_type ( - expr.get_receiver_expr ()->get_mappings ().get_hirid (), &receiver_ty); + expr.get_receiver_expr ().get_mappings ().get_hirid (), &receiver_ty); rust_assert (ok); if (receiver_ty->get_kind () == TyTy::TypeKind::ADT) @@ -487,7 +521,7 @@ UnsafeChecker::visit (FieldAccessExpr &expr) void UnsafeChecker::visit (ClosureExpr &expr) { - expr.get_expr ()->accept_vis (*this); + expr.get_expr ().accept_vis (*this); } void @@ -497,7 +531,7 @@ UnsafeChecker::visit (BlockExpr &expr) stmt->accept_vis (*this); if (expr.has_expr ()) - expr.get_final_expr ()->accept_vis (*this); + expr.get_final_expr ().accept_vis (*this); } void @@ -508,26 +542,26 @@ void UnsafeChecker::visit (BreakExpr &expr) { if (expr.has_break_expr ()) - expr.get_expr ()->accept_vis (*this); + expr.get_expr ().accept_vis (*this); } void UnsafeChecker::visit (RangeFromToExpr &expr) { - expr.get_from_expr ()->accept_vis (*this); - expr.get_to_expr ()->accept_vis (*this); + expr.get_from_expr ().accept_vis (*this); + expr.get_to_expr ().accept_vis (*this); } void UnsafeChecker::visit (RangeFromExpr &expr) { - expr.get_from_expr ()->accept_vis (*this); + expr.get_from_expr ().accept_vis (*this); } void UnsafeChecker::visit (RangeToExpr &expr) { - expr.get_to_expr ()->accept_vis (*this); + expr.get_to_expr ().accept_vis (*this); } void @@ -537,21 +571,21 @@ UnsafeChecker::visit (RangeFullExpr &) void UnsafeChecker::visit (RangeFromToInclExpr &expr) { - expr.get_from_expr ()->accept_vis (*this); - expr.get_to_expr ()->accept_vis (*this); + expr.get_from_expr ().accept_vis (*this); + expr.get_to_expr ().accept_vis (*this); } void UnsafeChecker::visit (RangeToInclExpr &expr) { - expr.get_to_expr ()->accept_vis (*this); + expr.get_to_expr ().accept_vis (*this); } void UnsafeChecker::visit (ReturnExpr &expr) { if (expr.has_return_expr ()) - expr.get_expr ()->accept_vis (*this); + expr.get_expr ().accept_vis (*this); } void @@ -559,7 +593,7 @@ UnsafeChecker::visit (UnsafeBlockExpr &expr) { unsafe_context.enter (expr.get_mappings ().get_hirid ()); - expr.get_block_expr ()->accept_vis (*this); + expr.get_block_expr ().accept_vis (*this); unsafe_context.exit (); } @@ -567,61 +601,45 @@ UnsafeChecker::visit (UnsafeBlockExpr &expr) void UnsafeChecker::visit (LoopExpr &expr) { - expr.get_loop_block ()->accept_vis (*this); + expr.get_loop_block ().accept_vis (*this); } void UnsafeChecker::visit (WhileLoopExpr &expr) { - expr.get_predicate_expr ()->accept_vis (*this); - expr.get_loop_block ()->accept_vis (*this); + expr.get_predicate_expr ().accept_vis (*this); + expr.get_loop_block ().accept_vis (*this); } void UnsafeChecker::visit (WhileLetLoopExpr &expr) { - expr.get_cond ()->accept_vis (*this); - expr.get_loop_block ()->accept_vis (*this); + expr.get_cond ().accept_vis (*this); + expr.get_loop_block ().accept_vis (*this); } void UnsafeChecker::visit (IfExpr &expr) { - expr.get_if_condition ()->accept_vis (*this); - expr.get_if_block ()->accept_vis (*this); + expr.get_if_condition ().accept_vis (*this); + expr.get_if_block ().accept_vis (*this); } void UnsafeChecker::visit (IfExprConseqElse &expr) { - expr.get_if_condition ()->accept_vis (*this); - expr.get_if_block ()->accept_vis (*this); - expr.get_else_block ()->accept_vis (*this); -} - -void -UnsafeChecker::visit (IfLetExpr &expr) -{ - expr.get_scrutinee_expr ()->accept_vis (*this); - expr.get_if_block ()->accept_vis (*this); -} - -void -UnsafeChecker::visit (IfLetExprConseqElse &expr) -{ - expr.get_scrutinee_expr ()->accept_vis (*this); - expr.get_if_block ()->accept_vis (*this); - - // TODO: Visit else expression + expr.get_if_condition ().accept_vis (*this); + expr.get_if_block ().accept_vis (*this); + expr.get_else_block ().accept_vis (*this); } void UnsafeChecker::visit (MatchExpr &expr) { - expr.get_scrutinee_expr ()->accept_vis (*this); + expr.get_scrutinee_expr ().accept_vis (*this); for (auto &match_arm : expr.get_match_cases ()) - match_arm.get_expr ()->accept_vis (*this); + match_arm.get_expr ().accept_vis (*this); } void @@ -698,7 +716,7 @@ UnsafeChecker::visit (Function &function) if (is_unsafe_fn) unsafe_context.enter (function.get_mappings ().get_hirid ()); - function.get_definition ()->accept_vis (*this); + function.get_definition ().accept_vis (*this); if (is_unsafe_fn) unsafe_context.exit (); @@ -746,27 +764,27 @@ UnsafeChecker::visit (Union &) void UnsafeChecker::visit (ConstantItem &const_item) { - const_item.get_expr ()->accept_vis (*this); + const_item.get_expr ().accept_vis (*this); } void UnsafeChecker::visit (StaticItem &static_item) { - static_item.get_expr ()->accept_vis (*this); + static_item.get_expr ().accept_vis (*this); } void UnsafeChecker::visit (TraitItemFunc &item) { - if (item.has_block_defined ()) - item.get_block_expr ()->accept_vis (*this); + if (item.has_definition ()) + item.get_block_expr ().accept_vis (*this); } void UnsafeChecker::visit (TraitItemConst &item) { if (item.has_expr ()) - item.get_expr ()->accept_vis (*this); + item.get_expr ().accept_vis (*this); } void @@ -784,7 +802,23 @@ UnsafeChecker::visit (Trait &trait) void UnsafeChecker::visit (ImplBlock &impl) { - // FIXME: Handle unsafe impls + bool safe = !impl.is_unsafe (); + // Check for unsafe-only attributes on generics and lifetimes + if (safe) + for (auto &parm : impl.get_generic_params ()) + { + for (auto o_attr : parm->get_outer_attrs ()) + { + rust_assert (!o_attr.is_inner_attribute ()); + + Rust::AST::SimplePath path = o_attr.get_path (); + if (path == Values::Attributes::MAY_DANGLE) + rust_error_at ( + o_attr.get_locus (), ErrorCode::E0569, + "use of %<may_dangle%> is unsafe and requires unsafe impl"); + } + } + for (auto &item : impl.get_impl_items ()) item->accept_vis (*this); } @@ -897,13 +931,13 @@ void UnsafeChecker::visit (LetStmt &stmt) { if (stmt.has_init_expr ()) - stmt.get_init_expr ()->accept_vis (*this); + stmt.get_init_expr ().accept_vis (*this); } void UnsafeChecker::visit (ExprStmt &stmt) { - stmt.get_expr ()->accept_vis (*this); + stmt.get_expr ().accept_vis (*this); } void diff --git a/gcc/rust/checks/errors/rust-unsafe-checker.h b/gcc/rust/checks/errors/rust-unsafe-checker.h index 1fa1fe0..8dc6ab7 100644 --- a/gcc/rust/checks/errors/rust-unsafe-checker.h +++ b/gcc/rust/checks/errors/rust-unsafe-checker.h @@ -110,12 +110,10 @@ private: virtual void visit (WhileLetLoopExpr &expr) override; virtual void visit (IfExpr &expr) override; virtual void visit (IfExprConseqElse &expr) override; - virtual void visit (IfLetExpr &expr) override; - virtual void visit (IfLetExprConseqElse &expr) override; virtual void visit (MatchExpr &expr) override; virtual void visit (AwaitExpr &expr) override; virtual void visit (AsyncBlockExpr &expr) override; - virtual void visit (InlineAsm &expr); + virtual void visit (InlineAsm &expr) override; virtual void visit (TypeParam ¶m) override; virtual void visit (ConstGenericParam ¶m) override; virtual void visit (LifetimeWhereClauseItem &item) override; diff --git a/gcc/rust/checks/lints/rust-lint-marklive.cc b/gcc/rust/checks/lints/rust-lint-marklive.cc index 24df933..4b524d7 100644 --- a/gcc/rust/checks/lints/rust-lint-marklive.cc +++ b/gcc/rust/checks/lints/rust-lint-marklive.cc @@ -124,7 +124,7 @@ MarkLive::visit (HIR::PathInExpression &expr) void MarkLive::visit (HIR::MethodCallExpr &expr) { - expr.get_receiver ()->accept_vis (*this); + expr.get_receiver ().accept_vis (*this); visit_path_segment (expr.get_method_name ()); for (auto &argument : expr.get_arguments ()) argument->accept_vis (*this); @@ -155,7 +155,17 @@ MarkLive::visit_path_segment (HIR::PathExprSegment seg) // // We should mark them alive all and ignoring other kind of segments. // If the segment we dont care then just return false is fine - if (!resolver->lookup_resolved_name (ast_node_id, &ref_node_id)) + if (flag_name_resolution_2_0) + { + auto &nr_ctx + = Resolver2_0::ImmutableNameResolutionContext::get ().resolver (); + + if (auto id = nr_ctx.lookup (ast_node_id)) + ref_node_id = *id; + else + return false; + } + else if (!resolver->lookup_resolved_name (ast_node_id, &ref_node_id)) { if (!resolver->lookup_resolved_type (ast_node_id, &ref_node_id)) return false; @@ -172,14 +182,14 @@ void MarkLive::visit (HIR::FieldAccessExpr &expr) { // visit receiver at first - expr.get_receiver_expr ()->accept_vis (*this); + expr.get_receiver_expr ().accept_vis (*this); // resolve the receiver back to ADT type TyTy::BaseType *receiver = nullptr; if (!tyctx->lookup_type ( - expr.get_receiver_expr ()->get_mappings ().get_hirid (), &receiver)) + expr.get_receiver_expr ().get_mappings ().get_hirid (), &receiver)) { - rust_error_at (expr.get_receiver_expr ()->get_locus (), + rust_error_at (expr.get_receiver_expr ().get_locus (), "unresolved type for receiver"); } @@ -211,7 +221,7 @@ MarkLive::visit (HIR::FieldAccessExpr &expr) rust_assert (ok); if (index >= variant->num_fields ()) { - rust_error_at (expr.get_receiver_expr ()->get_locus (), + rust_error_at (expr.get_receiver_expr ().get_locus (), "cannot access struct %s by index: %lu", adt->get_name ().c_str (), (unsigned long) index); return; @@ -226,15 +236,28 @@ void MarkLive::visit (HIR::TupleIndexExpr &expr) { // TODO: unused tuple field detection - expr.get_tuple_expr ()->accept_vis (*this); + expr.get_tuple_expr ().accept_vis (*this); } void MarkLive::visit (HIR::TypeAlias &alias) { - NodeId ast_node_id; - resolver->lookup_resolved_type ( - alias.get_type_aliased ()->get_mappings ().get_nodeid (), &ast_node_id); + NodeId ast_node_id = UNKNOWN_NODEID; + if (flag_name_resolution_2_0) + { + auto &nr_ctx + = Resolver2_0::ImmutableNameResolutionContext::get ().resolver (); + + if (auto id = nr_ctx.lookup ( + alias.get_type_aliased ().get_mappings ().get_nodeid ())) + ast_node_id = *id; + } + else + { + resolver->lookup_resolved_type ( + alias.get_type_aliased ().get_mappings ().get_nodeid (), &ast_node_id); + } + if (auto hid = mappings.lookup_node_to_hir (ast_node_id)) mark_hir_id (*hid); else diff --git a/gcc/rust/checks/lints/rust-lint-marklive.h b/gcc/rust/checks/lints/rust-lint-marklive.h index 92b4502..86d96fc 100644 --- a/gcc/rust/checks/lints/rust-lint-marklive.h +++ b/gcc/rust/checks/lints/rust-lint-marklive.h @@ -43,44 +43,44 @@ public: void visit (HIR::BorrowExpr &expr) override { - expr.get_expr ()->accept_vis (*this); + expr.get_expr ().accept_vis (*this); } void visit (HIR::DereferenceExpr &expr) override { - expr.get_expr ()->accept_vis (*this); + expr.get_expr ().accept_vis (*this); } void visit (HIR::NegationExpr &expr) override { - expr.get_expr ()->accept_vis (*this); + expr.get_expr ().accept_vis (*this); } void visit (HIR::LazyBooleanExpr &expr) override { - expr.get_lhs ()->accept_vis (*this); - expr.get_rhs ()->accept_vis (*this); + expr.get_lhs ().accept_vis (*this); + expr.get_rhs ().accept_vis (*this); } void visit (HIR::TypeCastExpr &expr) override { - expr.get_expr ()->accept_vis (*this); + expr.get_expr ().accept_vis (*this); } void visit (HIR::GroupedExpr &expr) override { - expr.get_expr_in_parens ()->accept_vis (*this); + expr.get_expr_in_parens ().accept_vis (*this); } void visit (HIR::ArrayExpr &expr) override { - expr.get_internal_elements ()->accept_vis (*this); + expr.get_internal_elements ().accept_vis (*this); } void visit (HIR::ArrayIndexExpr &expr) override { - expr.get_array_expr ()->accept_vis (*this); - expr.get_index_expr ()->accept_vis (*this); + expr.get_array_expr ().accept_vis (*this); + expr.get_index_expr ().accept_vis (*this); } void visit (HIR::ArrayElemsValues &expr) override @@ -107,57 +107,57 @@ public: } if (expr.has_expr ()) { - expr.get_final_expr ()->accept_vis (*this); + expr.get_final_expr ().accept_vis (*this); } } void visit (HIR::UnsafeBlockExpr &expr) override { - expr.get_block_expr ()->accept_vis (*this); + expr.get_block_expr ().accept_vis (*this); } void visit (HIR::LoopExpr &expr) override { - expr.get_loop_block ()->accept_vis (*this); + expr.get_loop_block ().accept_vis (*this); } void visit (HIR::BreakExpr &expr) override { if (expr.has_break_expr ()) - expr.get_expr ()->accept_vis (*this); + expr.get_expr ().accept_vis (*this); } void visit (HIR::WhileLoopExpr &expr) override { - expr.get_loop_block ()->accept_vis (*this); - expr.get_predicate_expr ()->accept_vis (*this); + expr.get_loop_block ().accept_vis (*this); + expr.get_predicate_expr ().accept_vis (*this); } void visit (HIR::Function &function) override { - function.get_definition ()->accept_vis (*this); + function.get_definition ().accept_vis (*this); } void visit (HIR::ReturnExpr &expr) override { if (expr.has_return_expr ()) - expr.get_expr ()->accept_vis (*this); + expr.get_expr ().accept_vis (*this); } void visit (HIR::WhileLetLoopExpr &expr) override { - expr.get_loop_block ()->accept_vis (*this); - expr.get_cond ()->accept_vis (*this); + expr.get_loop_block ().accept_vis (*this); + expr.get_cond ().accept_vis (*this); } void visit (HIR::ExprStmt &stmt) override { - stmt.get_expr ()->accept_vis (*this); + stmt.get_expr ().accept_vis (*this); } void visit (HIR::CallExpr &expr) override { - expr.get_fnexpr ()->accept_vis (*this); + expr.get_fnexpr ().accept_vis (*this); for (auto &argument : expr.get_arguments ()) argument->accept_vis (*this); } @@ -169,8 +169,8 @@ public: } void visit (HIR::ComparisonExpr &expr) override { - expr.get_lhs ()->accept_vis (*this); - expr.get_rhs ()->accept_vis (*this); + expr.get_lhs ().accept_vis (*this); + expr.get_rhs ().accept_vis (*this); } void visit (HIR::AssignmentExpr &expr) override @@ -187,33 +187,33 @@ public: void visit (HIR::IfExpr &expr) override { - expr.get_if_condition ()->accept_vis (*this); - expr.get_if_block ()->accept_vis (*this); + expr.get_if_condition ().accept_vis (*this); + expr.get_if_block ().accept_vis (*this); } void visit (HIR::IfExprConseqElse &expr) override { - expr.get_if_condition ()->accept_vis (*this); - expr.get_if_block ()->accept_vis (*this); - expr.get_else_block ()->accept_vis (*this); + expr.get_if_condition ().accept_vis (*this); + expr.get_if_block ().accept_vis (*this); + expr.get_else_block ().accept_vis (*this); } void visit (HIR::MatchExpr &expr) override { - expr.get_scrutinee_expr ()->accept_vis (*this); + expr.get_scrutinee_expr ().accept_vis (*this); std::vector<HIR::MatchCase> &cases = expr.get_match_cases (); for (auto &&caz : cases) { auto case_arm = caz.get_arm (); if (case_arm.has_match_arm_guard ()) - case_arm.get_guard_expr ()->accept_vis (*this); - caz.get_expr ()->accept_vis (*this); + case_arm.get_guard_expr ().accept_vis (*this); + caz.get_expr ().accept_vis (*this); } } void visit (HIR::TraitItemFunc &item) override { - item.get_block_expr ()->accept_vis (*this); + item.get_block_expr ().accept_vis (*this); } void visit (HIR::ImplBlock &impl) override @@ -228,7 +228,7 @@ public: { if (stmt.has_init_expr ()) { - stmt.get_init_expr ()->accept_vis (*this); + stmt.get_init_expr ().accept_vis (*this); } } @@ -247,18 +247,18 @@ public: stct.get_struct_name ().accept_vis (*this); if (stct.has_struct_base ()) { - stct.struct_base->base_struct->accept_vis (*this); + stct.get_struct_base ().get_base ().accept_vis (*this); } } virtual void visit (HIR::StructExprFieldIdentifierValue &field) override { - field.get_value ()->accept_vis (*this); + field.get_value ().accept_vis (*this); } void visit (HIR::StructExprStructBase &stct) override { - stct.get_struct_base ()->base_struct->accept_vis (*this); + stct.get_struct_base ().get_base ().accept_vis (*this); } void visit (HIR::Module &module) override @@ -269,7 +269,7 @@ public: void visit (HIR::ClosureExpr &expr) override { - expr.get_expr ()->accept_vis (*this); + expr.get_expr ().accept_vis (*this); } private: |