mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-23 21:01:08 +00:00
Bug 1719535 - Part 6. mach vendor rust for ICU4X crates. r=platform-i18n-reviewers,supply-chain-reviewers,firefox-build-system-reviewers,glandium,dminor
Depends on D167675 Differential Revision: https://phabricator.services.mozilla.com/D167674
This commit is contained in:
parent
d8a2fdf7da
commit
9fc41dc37c
@ -115,6 +115,11 @@ git = "https://github.com/mozilla/uniffi-rs.git"
|
||||
rev = "c0e64b839018728d8153ce1758d391b7782e2e21"
|
||||
replace-with = "vendored-sources"
|
||||
|
||||
[source."git+https://github.com/rust-diplomat/diplomat?rev=8d125999893fedfdf30595e97334c21ec4b18da9"]
|
||||
git = "https://github.com/rust-diplomat/diplomat"
|
||||
rev = "8d125999893fedfdf30595e97334c21ec4b18da9"
|
||||
replace-with = "vendored-sources"
|
||||
|
||||
[source."git+https://github.com/rust-minidump/minidump-writer.git?rev=a15bd5cab6a3de251c0c23264be14b977c0af09c"]
|
||||
git = "https://github.com/rust-minidump/minidump-writer.git"
|
||||
rev = "a15bd5cab6a3de251c0c23264be14b977c0af09c"
|
||||
@ -125,6 +130,11 @@ git = "https://github.com/rust-minidump/rust-minidump"
|
||||
rev = "87a29fba5e19cfae5ebf73a57ba31504a3872545"
|
||||
replace-with = "vendored-sources"
|
||||
|
||||
[source."git+https://github.com/unicode-org/icu4x?rev=14e9a3a9857be74582abe2dfa7ab799c5eaac873"]
|
||||
git = "https://github.com/unicode-org/icu4x"
|
||||
rev = "14e9a3a9857be74582abe2dfa7ab799c5eaac873"
|
||||
replace-with = "vendored-sources"
|
||||
|
||||
|
||||
# Take advantage of the fact that cargo will treat lines starting with #
|
||||
# as comments to add preprocessing directives. This file can thus by copied
|
||||
|
252
Cargo.lock
generated
252
Cargo.lock
generated
@ -1323,6 +1323,36 @@ dependencies = [
|
||||
"crypto-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "diplomat"
|
||||
version = "0.5.2"
|
||||
source = "git+https://github.com/rust-diplomat/diplomat?rev=8d125999893fedfdf30595e97334c21ec4b18da9#8d125999893fedfdf30595e97334c21ec4b18da9"
|
||||
dependencies = [
|
||||
"diplomat_core",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "diplomat-runtime"
|
||||
version = "0.5.2"
|
||||
source = "git+https://github.com/rust-diplomat/diplomat?rev=8d125999893fedfdf30595e97334c21ec4b18da9#8d125999893fedfdf30595e97334c21ec4b18da9"
|
||||
|
||||
[[package]]
|
||||
name = "diplomat_core"
|
||||
version = "0.5.2"
|
||||
source = "git+https://github.com/rust-diplomat/diplomat?rev=8d125999893fedfdf30595e97334c21ec4b18da9#8d125999893fedfdf30595e97334c21ec4b18da9"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
"smallvec",
|
||||
"strck_ident",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs"
|
||||
version = "4.0.0"
|
||||
@ -2561,6 +2591,116 @@ dependencies = [
|
||||
"want",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_capi"
|
||||
version = "1.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c29e1e7407081a5e425b295ef28526ca4c3cd7ce4475e1769c2863bb18b9305"
|
||||
dependencies = [
|
||||
"diplomat",
|
||||
"diplomat-runtime",
|
||||
"icu_locid",
|
||||
"icu_provider",
|
||||
"icu_provider_adapters",
|
||||
"icu_segmenter",
|
||||
"icu_testdata",
|
||||
"log",
|
||||
"tinystr",
|
||||
"writeable",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_collections"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef8302d8dfd6044d3ddb3f807a5ef3d7bbca9a574959c6d6e4dc39aa7012d0d5"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_locid"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3003f85dccfc0e238ff567693248c59153a46f4e6125ba4020b973cef4d1d335"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"litemap",
|
||||
"tinystr",
|
||||
"writeable",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_provider"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8dc312a7b6148f7dfe098047ae2494d12d4034f48ade58d4f353000db376e305"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_locid",
|
||||
"icu_provider_macros",
|
||||
"stable_deref_trait",
|
||||
"writeable",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_provider_adapters"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4ae1e2bd0c41728b77e7c46e9afdec5e2127d1eedacc684724667d50c126bd3"
|
||||
dependencies = [
|
||||
"icu_locid",
|
||||
"icu_provider",
|
||||
"tinystr",
|
||||
"yoke",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_provider_macros"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd8b728b9421e93eff1d9f8681101b78fa745e0748c95c655c83f337044a7e10"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.107",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_segmenter"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3300a7b6bf187be98a57264ad094f11f2e062c2e8263132af010ff522ee5495"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_collections",
|
||||
"icu_locid",
|
||||
"icu_provider",
|
||||
"num-traits",
|
||||
"utf8_iter",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_testdata"
|
||||
version = "1.2.0"
|
||||
dependencies = [
|
||||
"icu_collections",
|
||||
"icu_locid",
|
||||
"icu_provider",
|
||||
"icu_provider_adapters",
|
||||
"icu_segmenter",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "id-arena"
|
||||
version = "2.2.1"
|
||||
@ -2817,6 +2957,7 @@ dependencies = [
|
||||
"encoding_c",
|
||||
"encoding_c_mem",
|
||||
"gluesmith",
|
||||
"icu_capi",
|
||||
"mozglue-static",
|
||||
"smoosh",
|
||||
]
|
||||
@ -2954,6 +3095,12 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb"
|
||||
|
||||
[[package]]
|
||||
name = "libsqlite3-sys"
|
||||
version = "0.26.0"
|
||||
@ -2998,6 +3145,12 @@ version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
|
||||
|
||||
[[package]]
|
||||
name = "litemap"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a04a5b2b6f54acba899926491d0a6c59d98012938ca2ab5befb281c034e8f94"
|
||||
|
||||
[[package]]
|
||||
name = "lmdb-rkv"
|
||||
version = "0.14.0"
|
||||
@ -3827,6 +3980,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"libm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4985,6 +5139,22 @@ dependencies = [
|
||||
"xpcom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strck"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be91090ded9d8f979d9fe921777342d37e769e0b6b7296843a7a38247240e917"
|
||||
|
||||
[[package]]
|
||||
name = "strck_ident"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1c3802b169b3858a44667f221c9a0b3136e6019936ea926fc97fbad8af77202"
|
||||
dependencies = [
|
||||
"strck",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
@ -5288,6 +5458,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ac3f5b6856e931e15e07b478e98c8045239829a65f9156d4fa7e7788197a5ef"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -5504,9 +5675,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.8"
|
||||
version = "0.3.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
|
||||
checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
@ -5775,6 +5946,12 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utf8_iter"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64a8922555b9500e3d865caed19330172cd67cbf82203f1a3311d8c305cc9f33"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.3.0"
|
||||
@ -6314,6 +6491,12 @@ dependencies = [
|
||||
"euclid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "writeable"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60e49e42bdb1d5dc76f4cd78102f8f0714d32edfa3efb82286eb0f0b1fc0da0f"
|
||||
|
||||
[[package]]
|
||||
name = "xml-rs"
|
||||
version = "0.8.4"
|
||||
@ -6372,6 +6555,29 @@ dependencies = [
|
||||
"linked-hash-map",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yoke"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1848075a23a28f9773498ee9a0f2cf58fcbad4f8c0ccf84a210ab33c6ae495de"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"stable_deref_trait",
|
||||
"yoke-derive",
|
||||
"zerofrom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yoke-derive"
|
||||
version = "0.7.1"
|
||||
source = "git+https://github.com/unicode-org/icu4x?rev=14e9a3a9857be74582abe2dfa7ab799c5eaac873#14e9a3a9857be74582abe2dfa7ab799c5eaac873"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeitstempel"
|
||||
version = "0.1.1"
|
||||
@ -6383,6 +6589,48 @@ dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerofrom"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df54d76c3251de27615dfcce21e636c172dafb2549cd7fd93e21c66f6ca6bea2"
|
||||
dependencies = [
|
||||
"zerofrom-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerofrom-derive"
|
||||
version = "0.1.2"
|
||||
source = "git+https://github.com/unicode-org/icu4x?rev=14e9a3a9857be74582abe2dfa7ab799c5eaac873#14e9a3a9857be74582abe2dfa7ab799c5eaac873"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerovec"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "198f54134cd865f437820aa3b43d0ad518af4e68ee161b444cdd15d8e567c8ea"
|
||||
dependencies = [
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerovec-derive"
|
||||
version = "0.9.4"
|
||||
source = "git+https://github.com/unicode-org/icu4x?rev=14e9a3a9857be74582abe2dfa7ab799c5eaac873#14e9a3a9857be74582abe2dfa7ab799c5eaac873"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zip"
|
||||
version = "0.6.4"
|
||||
|
@ -207,6 +207,13 @@ tabs = { git = "https://github.com/mozilla/application-services", rev = "25972c3
|
||||
viaduct = { git = "https://github.com/mozilla/application-services", rev = "25972c388a4cf3a6d8256504f3a09b711db2fc6a" }
|
||||
webext-storage = { git = "https://github.com/mozilla/application-services", rev = "25972c388a4cf3a6d8256504f3a09b711db2fc6a" }
|
||||
|
||||
# ICU4X 1.2 with synstructure 0.13.x / syn 2.x. When updating to next version, this should be removed.
|
||||
diplomat = { git = "https://github.com/rust-diplomat/diplomat", rev = "8d125999893fedfdf30595e97334c21ec4b18da9" }
|
||||
diplomat-runtime = { git = "https://github.com/rust-diplomat/diplomat", rev = "8d125999893fedfdf30595e97334c21ec4b18da9" }
|
||||
yoke-derive = { git = "https://github.com/unicode-org/icu4x", rev = "14e9a3a9857be74582abe2dfa7ab799c5eaac873" }
|
||||
zerofrom-derive = { git = "https://github.com/unicode-org/icu4x", rev = "14e9a3a9857be74582abe2dfa7ab799c5eaac873" }
|
||||
zerovec-derive = { git = "https://github.com/unicode-org/icu4x", rev = "14e9a3a9857be74582abe2dfa7ab799c5eaac873" }
|
||||
|
||||
# Patch mio 0.6 to use winapi 0.3 and miow 0.3, getting rid of winapi 0.2.
|
||||
# There is not going to be new version of mio 0.6, mio now being >= 0.7.11.
|
||||
[patch.crates-io.mio]
|
||||
|
@ -397,6 +397,14 @@ start = "2022-12-16"
|
||||
end = "2024-06-21"
|
||||
notes = "Maintained by the Glean and Application Services teams"
|
||||
|
||||
[[wildcard-audits.utf8_iter]]
|
||||
who = "Makoto Kato <m_kato@ga2.so-net.ne.jp>"
|
||||
criteria = "safe-to-deploy"
|
||||
user-id = 4484 # Henri Sivonen (hsivonen)
|
||||
start = "2022-04-19"
|
||||
end = "2024-06-16"
|
||||
notes = "Maintained by Henri Sivonen who works at Mozilla."
|
||||
|
||||
[[wildcard-audits.webdriver]]
|
||||
who = "Henrik Skupin <mail@hskupin.info>"
|
||||
criteria = "safe-to-deploy"
|
||||
@ -1185,6 +1193,39 @@ who = "Mike Hommey <mh+mozilla@glandium.org>"
|
||||
criteria = "safe-to-deploy"
|
||||
delta = "0.10.3 -> 0.10.6"
|
||||
|
||||
[[audits.diplomat]]
|
||||
who = "Makoto Kato <m_kato@ga2.so-net.ne.jp>"
|
||||
criteria = "safe-to-deploy"
|
||||
version = "0.5.2"
|
||||
notes = "This crate is FFI wrapper generator using by ICU4X ffi libraries. This uses unsafe code to convert paramenters, I have reviewed this and generated headers."
|
||||
|
||||
[[audits.diplomat]]
|
||||
who = "Makoto Kato <m_kato@ga2.so-net.ne.jp>"
|
||||
criteria = "safe-to-deploy"
|
||||
delta = "0.5.2 -> 0.5.2@git:8d125999893fedfdf30595e97334c21ec4b18da9"
|
||||
|
||||
[[audits.diplomat-runtime]]
|
||||
who = "Makoto Kato <m_kato@ga2.so-net.ne.jp>"
|
||||
criteria = "safe-to-deploy"
|
||||
version = "0.5.2"
|
||||
notes = "This crate is FFI wrapper generator runtime using by ICU4X ffi libraries. This uses unsafe code for memory access of FFI. I have reviewed carefully."
|
||||
|
||||
[[audits.diplomat-runtime]]
|
||||
who = "Makoto Kato <m_kato@ga2.so-net.ne.jp>"
|
||||
criteria = "safe-to-deploy"
|
||||
delta = "0.5.2 -> 0.5.2@git:8d125999893fedfdf30595e97334c21ec4b18da9"
|
||||
|
||||
[[audits.diplomat_core]]
|
||||
who = "Makoto Kato <m_kato@ga2.so-net.ne.jp>"
|
||||
criteria = "safe-to-deploy"
|
||||
version = "0.5.2"
|
||||
notes = "This crate contains unsafe code, no network and no file access."
|
||||
|
||||
[[audits.diplomat_core]]
|
||||
who = "Makoto Kato <m_kato@ga2.so-net.ne.jp>"
|
||||
criteria = "safe-to-deploy"
|
||||
delta = "0.5.2 -> 0.5.2@git:8d125999893fedfdf30595e97334c21ec4b18da9"
|
||||
|
||||
[[audits.displaydoc]]
|
||||
who = "Makoto Kato <m_kato@ga2.so-net.ne.jp>"
|
||||
criteria = "safe-to-deploy"
|
||||
@ -1800,6 +1841,54 @@ who = "Mike Hommey <mh+mozilla@glandium.org>"
|
||||
criteria = "safe-to-run"
|
||||
delta = "0.14.23 -> 0.14.24"
|
||||
|
||||
[[audits.icu_capi]]
|
||||
who = "Makoto Kato <m_kato@ga2.so-net.ne.jp>"
|
||||
criteria = "safe-to-deploy"
|
||||
version = "1.2.2"
|
||||
notes = "This crate is C/C++ FFI for ICU4X using diplomat crate. no unsafe and no file access etc on this crate."
|
||||
|
||||
[[audits.icu_collections]]
|
||||
who = "Makoto Kato <m_kato@ga2.so-net.ne.jp>"
|
||||
criteria = "safe-to-deploy"
|
||||
version = "1.2.0"
|
||||
notes = "This crate is used by ICU4X for internal data structure. There is no fileaccess and network access. This uses unsafe block, but we confirm data is valid before."
|
||||
|
||||
[[audits.icu_locid]]
|
||||
who = "Makoto Kato <m_kato@ga2.so-net.ne.jp>"
|
||||
criteria = "safe-to-deploy"
|
||||
version = "1.2.0"
|
||||
notes = "This has unsafe block to handle ascii string in utf-8 string. I've vetted the one instance of unsafe code."
|
||||
|
||||
[[audits.icu_provider]]
|
||||
who = "Makoto Kato <m_kato@ga2.so-net.ne.jp>"
|
||||
criteria = "safe-to-deploy"
|
||||
version = "1.2.0"
|
||||
notes = "Although this has unsafe block, this has a commnet why this is safety and I audited code. Also, this doesn't have file access and network access."
|
||||
|
||||
[[audits.icu_provider_adapters]]
|
||||
who = "Makoto Kato <m_kato@ga2.so-net.ne.jp>"
|
||||
criteria = "safe-to-deploy"
|
||||
version = "1.2.0"
|
||||
notes = "This is one of ICU4X data provider crates that depends on data type. This has no unsafe code and uses no ambient capabilities."
|
||||
|
||||
[[audits.icu_provider_macros]]
|
||||
who = "Makoto Kato <m_kato@ga2.so-net.ne.jp>"
|
||||
criteria = "safe-to-deploy"
|
||||
version = "1.2.0"
|
||||
notes = "This crate is macros for ICU4X's data provider implementer. This has no unsafe code and uses no ambient capabilities."
|
||||
|
||||
[[audits.icu_segmenter]]
|
||||
who = "Makoto Kato <m_kato@ga2.so-net.ne.jp>"
|
||||
criteria = "safe-to-deploy"
|
||||
version = "1.2.1"
|
||||
notes = "Original authors are Makoto Kato and Ting-Yu Lin who work at Mozilla. This crate uses unsafe to matrix calculation, but it is safety to check length. And there is no filesystem / network access."
|
||||
|
||||
[[audits.icu_testdata]]
|
||||
who = "Makoto Kato <m_kato@ga2.so-net.ne.jp>"
|
||||
criteria = "safe-to-deploy"
|
||||
version = "1.2.0"
|
||||
notes = "This is just ICU4X data only that is generated by ICU4X datagen. Generated data is in unsafe block to use zero-copy implmentation, but it is safety."
|
||||
|
||||
[[audits.idna]]
|
||||
who = "Bobby Holley <bobbyholley@gmail.com>"
|
||||
criteria = "safe-to-deploy"
|
||||
@ -1896,6 +1985,12 @@ who = "Mike Hommey <mh+mozilla@glandium.org>"
|
||||
criteria = "safe-to-deploy"
|
||||
delta = "0.7.3 -> 0.7.4"
|
||||
|
||||
[[audits.libm]]
|
||||
who = "Makoto Kato <m_kato@ga2.so-net.ne.jp>"
|
||||
criteria = "safe-to-deploy"
|
||||
version = "0.2.6"
|
||||
notes = "This crate uses unsafe block, but this doesn't have network and file access. I audited code."
|
||||
|
||||
[[audits.libsqlite3-sys]]
|
||||
who = "Ben Dean-Kawamura <bdk@mozilla.com>"
|
||||
criteria = "safe-to-deploy"
|
||||
@ -1912,6 +2007,12 @@ who = "Mike Hommey <mh+mozilla@glandium.org>"
|
||||
criteria = "safe-to-run"
|
||||
delta = "0.5.4 -> 0.5.6"
|
||||
|
||||
[[audits.litemap]]
|
||||
who = "Makoto Kato <m_kato@ga2.so-net.ne.jp>"
|
||||
criteria = "safe-to-deploy"
|
||||
version = "0.7.0"
|
||||
notes = "This crete has no unsafe code, no file acceess and no network access."
|
||||
|
||||
[[audits.lmdb-rkv]]
|
||||
who = "Bobby Holley <bobbyholley@gmail.com>"
|
||||
criteria = "safe-to-deploy"
|
||||
@ -3019,6 +3120,18 @@ who = "Mike Hommey <mh+mozilla@glandium.org>"
|
||||
criteria = "safe-to-deploy"
|
||||
delta = "0.4.4 -> 0.4.7"
|
||||
|
||||
[[audits.strck]]
|
||||
who = "Makoto Kato <m_kato@ga2.so-net.ne.jp>"
|
||||
criteria = "safe-to-deploy"
|
||||
version = "0.1.2"
|
||||
notes = "This crate uses unsafe lock to keep invariant. I auditted code. Also, this doesn't have file access and network access."
|
||||
|
||||
[[audits.strck_ident]]
|
||||
who = "Makoto Kato <m_kato@ga2.so-net.ne.jp>"
|
||||
criteria = "safe-to-deploy"
|
||||
version = "0.1.2"
|
||||
notes = "This crate doesn't use unsafe block, network access and filesystem access."
|
||||
|
||||
[[audits.subtle]]
|
||||
who = "Simon Friedberger <simon@mozilla.com>"
|
||||
criteria = "safe-to-deploy"
|
||||
@ -3318,6 +3431,11 @@ who = "Mike Hommey <mh+mozilla@glandium.org>"
|
||||
criteria = "safe-to-deploy"
|
||||
delta = "0.9.0 -> 0.9.1"
|
||||
|
||||
[[audits.unicode-bidi]]
|
||||
who = "Makoto Kato <m_kato@ga2.so-net.ne.jp>"
|
||||
criteria = "safe-to-deploy"
|
||||
delta = "0.3.8 -> 0.3.13"
|
||||
|
||||
[[audits.unicode-ident]]
|
||||
who = "Mike Hommey <mh+mozilla@glandium.org>"
|
||||
criteria = "safe-to-deploy"
|
||||
@ -4013,12 +4131,54 @@ criteria = "safe-to-deploy"
|
||||
version = "0.1.0"
|
||||
notes = "Written and maintained by Gfx team at Mozilla."
|
||||
|
||||
[[audits.writeable]]
|
||||
who = "Makoto Kato <m_kato@ga2.so-net.ne.jp>"
|
||||
criteria = "safe-to-deploy"
|
||||
version = "0.5.2"
|
||||
notes = "writeable is a variation of fmt::Write with sink version. This uses `unsafe` block to handle potentially-invalid UTF-8 character. I've vetted the one instance of unsafe code."
|
||||
|
||||
[[audits.xmldecl]]
|
||||
who = "Henri Sivonen <hsivonen@hsivonen.fi>"
|
||||
criteria = "safe-to-deploy"
|
||||
version = "0.2.0"
|
||||
notes = "I, Henri Sivonen, wrote this crate myself for Gecko even though it's published on crates.io."
|
||||
|
||||
[[audits.yoke]]
|
||||
who = "Makoto Kato <m_kato@ga2.so-net.ne.jp>"
|
||||
criteria = "safe-to-deploy"
|
||||
version = "0.7.1"
|
||||
notes = "This crate is for zero-copy serialization for ICU4X data structure, and maintained by ICU4X team. Since this uses unsafe block for serialization, I audited code."
|
||||
|
||||
[[audits.yoke-derive]]
|
||||
who = "Makoto Kato <m_kato@ga2.so-net.ne.jp>"
|
||||
criteria = "safe-to-deploy"
|
||||
version = "0.7.1@git:14e9a3a9857be74582abe2dfa7ab799c5eaac873"
|
||||
notes = "This crate is a helper for yoke crate that is ICU4X data structure, and maintained by ICU4X team. Since this uses unsafe block for serialization, all has the comment why this uses unsafe and I audited code."
|
||||
|
||||
[[audits.zerofrom]]
|
||||
who = "Makoto Kato <m_kato@ga2.so-net.ne.jp>"
|
||||
criteria = "safe-to-deploy"
|
||||
version = "0.1.2"
|
||||
notes = "This crate is zero-copy version of \"From\". This has no unsafe code and uses no ambient capabilities."
|
||||
|
||||
[[audits.zerofrom-derive]]
|
||||
who = "Makoto Kato <m_kato@ga2.so-net.ne.jp>"
|
||||
criteria = "safe-to-deploy"
|
||||
version = "0.1.2@git:14e9a3a9857be74582abe2dfa7ab799c5eaac873"
|
||||
notes = "This is custom derives for `ZeroFrom` that is from zerofrom crate. This has no unsafe code and uses no ambient capabilities."
|
||||
|
||||
[[audits.zerovec]]
|
||||
who = "Makoto Kato <m_kato@ga2.so-net.ne.jp>"
|
||||
criteria = "safe-to-deploy"
|
||||
version = "0.9.4"
|
||||
notes = "This crate is zero-copy data structure implmentation. Although this uses unsafe block in several code, it requires for zero-copy. And this has a comment in code why this uses unsafe and I audited code."
|
||||
|
||||
[[audits.zerovec-derive]]
|
||||
who = "Makoto Kato <m_kato@ga2.so-net.ne.jp>"
|
||||
criteria = "safe-to-deploy"
|
||||
version = "0.9.4@git:14e9a3a9857be74582abe2dfa7ab799c5eaac873"
|
||||
notes = "This is custom derives for `ZeroVec` that is from zerovec crate. Although this uses unsafe block for zero-copy, this has a comment in code why this uses unsafe and I audited code."
|
||||
|
||||
[[audits.zip]]
|
||||
who = "Mike Hommey <mh+mozilla@glandium.org>"
|
||||
criteria = "safe-to-run"
|
||||
|
@ -39,6 +39,18 @@ notes = "This is a pinned version of the upstream code, presumably to get a fix
|
||||
audit-as-crates-io = true
|
||||
notes = "This is upstream plus a warning fix from bug 1823866."
|
||||
|
||||
[policy.diplomat]
|
||||
audit-as-crates-io = true
|
||||
notes = "Upstream version not to use syn 1.x"
|
||||
|
||||
[policy.diplomat-runtime]
|
||||
audit-as-crates-io = true
|
||||
notes = "Upstream version not to use syn 1.x"
|
||||
|
||||
[policy.diplomat_core]
|
||||
audit-as-crates-io = true
|
||||
notes = "Upstream version not to use syn 1.x"
|
||||
|
||||
[policy.firefox-on-glean]
|
||||
audit-as-crates-io = false
|
||||
notes = "The crates.io version of this is just a placeholder to allow public crates to depend on firefox-on-glean."
|
||||
@ -64,6 +76,10 @@ notes = "Used for fuzzing."
|
||||
criteria = "safe-to-run"
|
||||
notes = "Used for testing."
|
||||
|
||||
[policy.icu_testdata]
|
||||
audit-as-crates-io = false
|
||||
notes = "Customized ICU4X baked data only that Gecko wants"
|
||||
|
||||
[policy.l10nregistry]
|
||||
dependency-criteria = { fluent-testing = "safe-to-run", tokio = "safe-to-run" }
|
||||
notes = "This crate has two testing-only dependencies which are specified as regular-but-optional rather than a dev-dependencies, because they need to be available to both benchmarks and integration tests."
|
||||
@ -221,6 +237,18 @@ notes = "Upstream project which we pin."
|
||||
[policy.wr_malloc_size_of]
|
||||
audit-as-crates-io = false
|
||||
|
||||
[policy.yoke-derive]
|
||||
audit-as-crates-io = true
|
||||
notes = "Upstream version not to use syn 1.x"
|
||||
|
||||
[policy.zerofrom-derive]
|
||||
audit-as-crates-io = true
|
||||
notes = "Upstream version not to use syn 1.x"
|
||||
|
||||
[policy.zerovec-derive]
|
||||
audit-as-crates-io = true
|
||||
notes = "Upstream version not to use syn 1.x"
|
||||
|
||||
[[exemptions.ahash]]
|
||||
version = "0.7.6"
|
||||
criteria = "safe-to-deploy"
|
||||
|
@ -574,6 +574,13 @@ user-id = 48
|
||||
user-login = "badboy"
|
||||
user-name = "Jan-Erik Rediger"
|
||||
|
||||
[[publisher.utf8_iter]]
|
||||
version = "1.0.3"
|
||||
when = "2022-09-09"
|
||||
user-id = 4484
|
||||
user-login = "hsivonen"
|
||||
user-name = "Henri Sivonen"
|
||||
|
||||
[[publisher.walkdir]]
|
||||
version = "2.3.2"
|
||||
when = "2021-03-22"
|
||||
|
1
third_party/rust/diplomat-runtime/.cargo-checksum.json
vendored
Normal file
1
third_party/rust/diplomat-runtime/.cargo-checksum.json
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"files":{"Cargo.toml":"7fc35f8643067872335fe85cf4a6a4289c1b82156cf1216d223b44513b8d0d98","LICENSE-APACHE":"639c20c7f14fb122750d5ad1a6cfb116d9bf8d103e709ee40949e5a12a731666","LICENSE-MIT":"3337fe6e4a3830ad87c23cb9d6d750f9a1e5c45efc08de9c76c1a207fc6966c4","src/lib.rs":"6c019d412bb5534abd3869a16574599dcff730b89a576c3cf4c5590a1d449d17","src/result.rs":"2b07ce3cbb02a141abc35742f307cc4ef2b097ce610e04d92a50c469f354401d","src/wasm_glue.rs":"f26aa119aa291a3cfafc16b73f713a391718294a51e2c656d0093cf1fd2befac","src/writeable.rs":"80d8a93feba545fcb3150b8daf546f3e060b0c8c11f13917409dd455abe0c3bd"},"package":null}
|
34
third_party/rust/diplomat-runtime/Cargo.toml
vendored
Normal file
34
third_party/rust/diplomat-runtime/Cargo.toml
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
# 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 = "2018"
|
||||
name = "diplomat-runtime"
|
||||
version = "0.5.2"
|
||||
authors = [
|
||||
"Shadaj Laddad <shadaj@users.noreply.github.com>",
|
||||
"Manish Goregaokar <manishsmail@gmail.com>",
|
||||
"Quinn Okabayashi <QnnOkabayashi@users.noreply.github.com>",
|
||||
]
|
||||
description = "Common runtime utilities used by diplomat codegen"
|
||||
documentation = "https://docs.rs/diplomat_core/"
|
||||
keywords = [
|
||||
"ffi",
|
||||
"codegen",
|
||||
]
|
||||
categories = ["development-tools"]
|
||||
license = "MIT/Apache-2.0"
|
||||
repository = "https://github.com/rust-diplomat/diplomat"
|
||||
|
||||
[lib]
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
201
third_party/rust/diplomat-runtime/LICENSE-APACHE
vendored
Normal file
201
third_party/rust/diplomat-runtime/LICENSE-APACHE
vendored
Normal file
@ -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 2022 The Diplomat Developers
|
||||
|
||||
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.
|
27
third_party/rust/diplomat-runtime/LICENSE-MIT
vendored
Normal file
27
third_party/rust/diplomat-runtime/LICENSE-MIT
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 The Diplomat 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.
|
33
third_party/rust/diplomat-runtime/src/lib.rs
vendored
Normal file
33
third_party/rust/diplomat-runtime/src/lib.rs
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
#![cfg_attr(not(any(target_arch = "wasm32")), no_std)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::alloc::Layout;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
mod wasm_glue;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub use wasm_glue::{console_log, console_trace, console_warn};
|
||||
|
||||
mod writeable;
|
||||
pub use writeable::DiplomatWriteable;
|
||||
|
||||
mod result;
|
||||
pub use result::DiplomatResult;
|
||||
|
||||
/// Allocates a buffer of a given size in Rust's memory.
|
||||
///
|
||||
/// # Safety
|
||||
/// - The allocated buffer must be freed with [`diplomat_free()`].
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn diplomat_alloc(size: usize, align: usize) -> *mut u8 {
|
||||
alloc::alloc::alloc(Layout::from_size_align(size, align).unwrap())
|
||||
}
|
||||
|
||||
/// Frees a buffer that was allocated in Rust's memory.
|
||||
/// # Safety
|
||||
/// - `ptr` must be a pointer to a valid buffer allocated by [`diplomat_alloc()`].
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn diplomat_free(ptr: *mut u8, size: usize, align: usize) {
|
||||
alloc::alloc::dealloc(ptr, Layout::from_size_align(size, align).unwrap())
|
||||
}
|
60
third_party/rust/diplomat-runtime/src/result.rs
vendored
Normal file
60
third_party/rust/diplomat-runtime/src/result.rs
vendored
Normal file
@ -0,0 +1,60 @@
|
||||
use core::mem::ManuallyDrop;
|
||||
|
||||
#[repr(C)]
|
||||
union DiplomatResultValue<T, E> {
|
||||
ok: ManuallyDrop<T>,
|
||||
err: ManuallyDrop<E>,
|
||||
}
|
||||
|
||||
/// A `Result`-like type that can be passed across the FFI boundary
|
||||
/// as a value. Use this type when returning a result from an FFI
|
||||
/// function instead of `Result`.
|
||||
#[repr(C)]
|
||||
pub struct DiplomatResult<T, E> {
|
||||
value: DiplomatResultValue<T, E>,
|
||||
pub is_ok: bool,
|
||||
}
|
||||
|
||||
impl<T, E> Drop for DiplomatResult<T, E> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
if self.is_ok {
|
||||
let _ = ManuallyDrop::take(&mut self.value.ok);
|
||||
} else {
|
||||
let _ = ManuallyDrop::take(&mut self.value.err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> From<Result<T, E>> for DiplomatResult<T, E> {
|
||||
fn from(result: Result<T, E>) -> Self {
|
||||
match result {
|
||||
Result::Ok(ok) => DiplomatResult {
|
||||
value: DiplomatResultValue {
|
||||
ok: ManuallyDrop::new(ok),
|
||||
},
|
||||
is_ok: true,
|
||||
},
|
||||
|
||||
Result::Err(err) => DiplomatResult {
|
||||
value: DiplomatResultValue {
|
||||
err: ManuallyDrop::new(err),
|
||||
},
|
||||
is_ok: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> From<DiplomatResult<T, E>> for Result<T, E> {
|
||||
fn from(mut result: DiplomatResult<T, E>) -> Result<T, E> {
|
||||
unsafe {
|
||||
if result.is_ok {
|
||||
Ok(ManuallyDrop::take(&mut result.value.ok))
|
||||
} else {
|
||||
Err(ManuallyDrop::take(&mut result.value.err))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
43
third_party/rust/diplomat-runtime/src/wasm_glue.rs
vendored
Normal file
43
third_party/rust/diplomat-runtime/src/wasm_glue.rs
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
// minimal WASM logger based on https://github.com/DeMille/wasm-glue
|
||||
extern "C" {
|
||||
fn trace_js(ptr: *const u8, len: usize);
|
||||
fn warn_js(ptr: *const u8, len: usize);
|
||||
fn log_js(ptr: *const u8, len: usize);
|
||||
}
|
||||
|
||||
/// Throw an exception.
|
||||
pub fn console_trace(msg: &str) {
|
||||
unsafe {
|
||||
trace_js(msg.as_ptr(), msg.len());
|
||||
}
|
||||
}
|
||||
|
||||
/// Write a message to `console.warn`.
|
||||
pub fn console_warn(msg: &str) {
|
||||
unsafe { warn_js(msg.as_ptr(), msg.len()) }
|
||||
}
|
||||
|
||||
/// Write a message to `console.log`.
|
||||
pub fn console_log(msg: &str) {
|
||||
unsafe { log_js(msg.as_ptr(), msg.len()) }
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn diplomat_init() {
|
||||
#[cfg(debug_assertions)]
|
||||
// Sets a custom panic hook using `trace_js`, which by default crates a JS error
|
||||
std::panic::set_hook(Box::new(|info| {
|
||||
let file = info.location().unwrap().file();
|
||||
let line = info.location().unwrap().line();
|
||||
let col = info.location().unwrap().column();
|
||||
|
||||
let msg = match info.payload().downcast_ref::<&'static str>() {
|
||||
Some(&s) => s,
|
||||
None => match info.payload().downcast_ref::<String>() {
|
||||
Some(s) => s.as_str(),
|
||||
None => "Box<Any>",
|
||||
},
|
||||
};
|
||||
console_trace(&format!("Panicked at '{msg}', {file}:{line}:{col}"));
|
||||
}));
|
||||
}
|
189
third_party/rust/diplomat-runtime/src/writeable.rs
vendored
Normal file
189
third_party/rust/diplomat-runtime/src/writeable.rs
vendored
Normal file
@ -0,0 +1,189 @@
|
||||
use alloc::boxed::Box;
|
||||
use alloc::vec::Vec;
|
||||
use core::ffi::c_void;
|
||||
use core::{fmt, ptr};
|
||||
|
||||
/// An object that can one can write UTF-8 strings to
|
||||
///
|
||||
/// This allows the C API to write to arbitrary kinds of objects, for example a
|
||||
/// C++ std::string or a char buffer.
|
||||
///
|
||||
/// The way to use this object is to fill out the `buf`, `len`, `cap` fields with
|
||||
/// appropriate values for the buffer, its current length, and its current capacity,
|
||||
/// and `flush` and `grow` with appropriate callbacks (using `context` to reference any
|
||||
/// state they need). This object will be passed by mutable reference to the Rust side,
|
||||
/// and Rust will write to it, calling `grow()` as necessary. Once done, it will call `flush()`
|
||||
/// to update any state on `context` (e.g. adding a null terminator, updating the length).
|
||||
/// The object on the foreign side will be directly usable after this, the foreign side
|
||||
/// need not perform additional state updates after passing an [`DiplomatWriteable`] to
|
||||
/// a function.
|
||||
///
|
||||
/// [`diplomat_simple_writeable()`] can be used to write to a fixed-size char buffer.
|
||||
///
|
||||
/// May be extended in the future to support further invariants
|
||||
///
|
||||
/// DiplomatWriteable will not perform any cleanup on `context` or `buf`, these are logically
|
||||
/// "borrows" from the FFI side.
|
||||
///
|
||||
/// # Safety invariants:
|
||||
/// - `flush()` and `grow()` will be passed `self` including `context` and it should always be safe to do so.
|
||||
/// `context` may be null, however `flush()` and `grow()` must then be ready to receive it as such.
|
||||
/// - `buf` must be `cap` bytes long
|
||||
/// - `grow()` must either return false or update `buf` and `cap` for a valid buffer
|
||||
/// of at least the requested buffer size
|
||||
/// - `DiplomatWriteable::flush()` will be automatically called by Diplomat. `flush()` might also be called
|
||||
/// (erroneously) on the Rust side (it's a public method), so it must be idempotent.
|
||||
#[repr(C)]
|
||||
pub struct DiplomatWriteable {
|
||||
/// Context pointer for additional data needed by `grow()` and `flush()`. May be `null`.
|
||||
///
|
||||
/// The pointer may reference structured data on the foreign side,
|
||||
/// such as C++ std::string, used to reallocate buf.
|
||||
context: *mut c_void,
|
||||
/// The raw string buffer, which will be mutated on the Rust side.
|
||||
buf: *mut u8,
|
||||
/// The current filled size of the buffer
|
||||
len: usize,
|
||||
/// The current capacity of the buffer
|
||||
cap: usize,
|
||||
/// Called by Rust to indicate that there is no more data to write.
|
||||
///
|
||||
/// May be called multiple times.
|
||||
///
|
||||
/// Arguments:
|
||||
/// - `self` (`*mut DiplomatWriteable`): This `DiplomatWriteable`
|
||||
flush: extern "C" fn(*mut DiplomatWriteable),
|
||||
/// Called by Rust to request more capacity in the buffer. The implementation should allocate a new
|
||||
/// buffer and copy the contents of the old buffer into the new buffer, updating `self.buf` and `self.cap`
|
||||
///
|
||||
/// Arguments:
|
||||
/// - `self` (`*mut DiplomatWriteable`): This `DiplomatWriteable`
|
||||
/// - `capacity` (`usize`): The requested capacity.
|
||||
///
|
||||
/// Returns: `true` if the allocation succeeded. Should not update any state if it failed.
|
||||
grow: extern "C" fn(*mut DiplomatWriteable, usize) -> bool,
|
||||
}
|
||||
|
||||
impl DiplomatWriteable {
|
||||
/// Call this function before releasing the buffer to C
|
||||
pub fn flush(&mut self) {
|
||||
(self.flush)(self);
|
||||
}
|
||||
}
|
||||
impl fmt::Write for DiplomatWriteable {
|
||||
fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
|
||||
let needed_len = self.len + s.len();
|
||||
if needed_len > self.cap {
|
||||
let success = (self.grow)(self, needed_len);
|
||||
if !success {
|
||||
return Err(fmt::Error);
|
||||
}
|
||||
}
|
||||
debug_assert!(needed_len <= self.cap);
|
||||
unsafe {
|
||||
ptr::copy_nonoverlapping(s.as_bytes().as_ptr(), self.buf.add(self.len), s.len());
|
||||
}
|
||||
self.len = needed_len;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Create an `DiplomatWriteable` that can write to a fixed-length stack allocated `u8` buffer.
|
||||
///
|
||||
/// Once done, this will append a null terminator to the written string.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// - `buf` must be a valid pointer to a region of memory that can hold at `buf_size` bytes
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn diplomat_simple_writeable(
|
||||
buf: *mut u8,
|
||||
buf_size: usize,
|
||||
) -> DiplomatWriteable {
|
||||
extern "C" fn grow(_this: *mut DiplomatWriteable, _cap: usize) -> bool {
|
||||
false
|
||||
}
|
||||
extern "C" fn flush(this: *mut DiplomatWriteable) {
|
||||
unsafe {
|
||||
debug_assert!((*this).len <= (*this).cap);
|
||||
let buf = (*this).buf;
|
||||
ptr::write(buf.add((*this).len), 0)
|
||||
}
|
||||
}
|
||||
DiplomatWriteable {
|
||||
context: ptr::null_mut(),
|
||||
buf,
|
||||
len: 0,
|
||||
// keep an extra byte in our pocket for the null terminator
|
||||
cap: buf_size - 1,
|
||||
flush,
|
||||
grow,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create an [`DiplomatWriteable`] that can write to a dynamically allocated buffer managed by Rust.
|
||||
///
|
||||
/// Use [`diplomat_buffer_writeable_destroy()`] to free the writable and its underlying buffer.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn diplomat_buffer_writeable_create(cap: usize) -> *mut DiplomatWriteable {
|
||||
extern "C" fn grow(this: *mut DiplomatWriteable, new_cap: usize) -> bool {
|
||||
unsafe {
|
||||
let this = this.as_mut().unwrap();
|
||||
let mut vec = Vec::from_raw_parts(this.buf, 0, this.cap);
|
||||
vec.reserve(new_cap);
|
||||
this.cap = vec.capacity();
|
||||
this.buf = vec.as_mut_ptr();
|
||||
core::mem::forget(vec);
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
extern "C" fn flush(_: *mut DiplomatWriteable) {}
|
||||
|
||||
let mut vec = Vec::<u8>::with_capacity(cap);
|
||||
let ret = DiplomatWriteable {
|
||||
context: ptr::null_mut(),
|
||||
buf: vec.as_mut_ptr(),
|
||||
len: 0,
|
||||
cap,
|
||||
flush,
|
||||
grow,
|
||||
};
|
||||
|
||||
core::mem::forget(vec);
|
||||
Box::into_raw(Box::new(ret))
|
||||
}
|
||||
|
||||
/// Grabs a pointer to the underlying buffer of a writable.
|
||||
///
|
||||
/// # Safety
|
||||
/// - The returned pointer is valid until the passed writable is destroyed.
|
||||
/// - `this` must be a pointer to a valid [`DiplomatWriteable`] constructed by
|
||||
/// [`diplomat_buffer_writeable_create()`].
|
||||
#[no_mangle]
|
||||
pub extern "C" fn diplomat_buffer_writeable_get_bytes(this: &DiplomatWriteable) -> *mut u8 {
|
||||
this.buf
|
||||
}
|
||||
|
||||
/// Gets the length in bytes of the content written to the writable.
|
||||
///
|
||||
/// # Safety
|
||||
/// - `this` must be a pointer to a valid [`DiplomatWriteable`] constructed by
|
||||
/// [`diplomat_buffer_writeable_create()`].
|
||||
#[no_mangle]
|
||||
pub extern "C" fn diplomat_buffer_writeable_len(this: &DiplomatWriteable) -> usize {
|
||||
this.len
|
||||
}
|
||||
|
||||
/// Destructor for Rust-memory backed writables.
|
||||
///
|
||||
/// # Safety
|
||||
/// - `this` must be a pointer to a valid [`DiplomatWriteable`] constructed by
|
||||
/// [`diplomat_buffer_writeable_create()`].
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn diplomat_buffer_writeable_destroy(this: *mut DiplomatWriteable) {
|
||||
let this = Box::from_raw(this);
|
||||
let vec = Vec::from_raw_parts(this.buf, 0, this.cap);
|
||||
drop(vec);
|
||||
drop(this);
|
||||
}
|
1
third_party/rust/diplomat/.cargo-checksum.json
vendored
Normal file
1
third_party/rust/diplomat/.cargo-checksum.json
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"files":{"Cargo.toml":"8ae49bf1abfabd0c4013a0629f806795773baeca9e99605e485c6d9060d5f18e","LICENSE-APACHE":"639c20c7f14fb122750d5ad1a6cfb116d9bf8d103e709ee40949e5a12a731666","LICENSE-MIT":"3337fe6e4a3830ad87c23cb9d6d750f9a1e5c45efc08de9c76c1a207fc6966c4","src/enum_convert.rs":"c0068cb8b563043030186cd9a0be6a4eac1a5f1761fe3646a99528e6d3dc5f54","src/lib.rs":"e606ade5f7cc5b43f56b07511b34fe5c0eb2e53640a347d17585062bf9cdea7c","src/snapshots/diplomat__tests__cfgd_struct.snap":"2874497e83ba3f43541d90869bfa428973c5b23df7fec2826e187d530f1a620b","src/snapshots/diplomat__tests__cfged_method-2.snap":"45f4c58a153cfc8a01d24fcd4474cd7ff84e34ed8b75e718143c16edd884c510","src/snapshots/diplomat__tests__cfged_method.snap":"b478d2d14e01209b45032b092bf91a2068f08f704b2395bc3ebd2ada21141077","src/snapshots/diplomat__tests__method_taking_mutable_slice.snap":"eafa335a999e416a044d32106e096ef57b7f41fb2f74b03d4adce13e7179b03d","src/snapshots/diplomat__tests__method_taking_mutable_str.snap":"e4c65337861a78b3c9762545fcdbccc1169bab9183ff750fc467a5367dba9c56","src/snapshots/diplomat__tests__method_taking_slice.snap":"eb2d7d00381ddef71411c170ce27d2490acfe9322bc3de397fe1cedcedeeee7b","src/snapshots/diplomat__tests__method_taking_str.snap":"92aa38d8618f0d52d5bc967a8d67a87f4b9cdc4f8651c624a9cc7b73033dbaa4","src/snapshots/diplomat__tests__mod_with_enum.snap":"fc225e910aa1afe496eb8d4da4342894f7786c53e12725b2f70018cf5230dddc","src/snapshots/diplomat__tests__mod_with_rust_result.snap":"48e30564d2cf0477db7062c58842b264d0cfec1d635e7dbaf12c12a2e9f1ab31","src/snapshots/diplomat__tests__mod_with_writeable_result.snap":"6ddbc34dbf7d362366cddda70c87a2f25d7ba0821ccbab90b5a79748f1064593","src/snapshots/diplomat__tests__multilevel_borrows.snap":"708fb54c2c3b8498aac83c20af891e12212874b94691fd734fd57f500ce54666","src/snapshots/diplomat__tests__self_params.snap":"1f6652799973e4afa751afebd6306e71ea439640cc1cae9848c5757d060bf699","src/transparent_convert.rs":"dde901986a6709a21f359596e85bc4fd009bb645c79b698d5af8e2a603996ac4"},"package":null}
|
52
third_party/rust/diplomat/Cargo.toml
vendored
Normal file
52
third_party/rust/diplomat/Cargo.toml
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
# 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 = "2018"
|
||||
name = "diplomat"
|
||||
version = "0.5.2"
|
||||
authors = [
|
||||
"Shadaj Laddad <shadaj@users.noreply.github.com>",
|
||||
"Manish Goregaokar <manishsmail@gmail.com>",
|
||||
"Quinn Okabayashi <QnnOkabayashi@users.noreply.github.com>",
|
||||
]
|
||||
description = "The diplomat FFI generation macro"
|
||||
documentation = "https://docs.rs/diplomat_core/"
|
||||
keywords = [
|
||||
"ffi",
|
||||
"codegen",
|
||||
]
|
||||
categories = ["development-tools"]
|
||||
license = "MIT/Apache-2.0"
|
||||
repository = "https://github.com/rust-diplomat/diplomat"
|
||||
|
||||
[lib]
|
||||
path = "src/lib.rs"
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
proc-macro2 = "1.0.27"
|
||||
quote = "1.0"
|
||||
|
||||
[dependencies.diplomat_core]
|
||||
version = "0.5.2"
|
||||
path = "../core"
|
||||
|
||||
[dependencies.syn]
|
||||
version = "2.0"
|
||||
features = [
|
||||
"full",
|
||||
"extra-traits",
|
||||
]
|
||||
|
||||
[dev-dependencies]
|
||||
insta = "1.7.1"
|
||||
tempfile = "3.2.0"
|
201
third_party/rust/diplomat/LICENSE-APACHE
vendored
Normal file
201
third_party/rust/diplomat/LICENSE-APACHE
vendored
Normal file
@ -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 2022 The Diplomat Developers
|
||||
|
||||
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.
|
27
third_party/rust/diplomat/LICENSE-MIT
vendored
Normal file
27
third_party/rust/diplomat/LICENSE-MIT
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 The Diplomat 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.
|
85
third_party/rust/diplomat/src/enum_convert.rs
vendored
Normal file
85
third_party/rust/diplomat/src/enum_convert.rs
vendored
Normal file
@ -0,0 +1,85 @@
|
||||
use quote::{quote, ToTokens};
|
||||
use syn::parse::{Parse, ParseStream};
|
||||
use syn::*;
|
||||
|
||||
// An attribute that is a list of idents
|
||||
pub struct EnumConvertAttribute {
|
||||
path: Path,
|
||||
|
||||
needs_wildcard: bool,
|
||||
}
|
||||
|
||||
impl Parse for EnumConvertAttribute {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let paths = input.parse_terminated(Path::parse, Token![,])?;
|
||||
if paths.is_empty() {
|
||||
return Err(input.error("#[diplomat::enum_convert] needs a path argument"));
|
||||
}
|
||||
let needs_wildcard = if paths.len() == 2 {
|
||||
if let Some(ident) = paths[1].get_ident() {
|
||||
if ident == "needs_wildcard" {
|
||||
true
|
||||
} else {
|
||||
return Err(input.error(
|
||||
"#[diplomat::enum_convert] only recognizes needs_wildcard keyword",
|
||||
));
|
||||
}
|
||||
} else {
|
||||
return Err(
|
||||
input.error("#[diplomat::enum_convert] only recognizes needs_wildcard keyword")
|
||||
);
|
||||
}
|
||||
} else if paths.len() > 1 {
|
||||
return Err(input.error("#[diplomat::enum_convert] only supports up to two arguments"));
|
||||
} else {
|
||||
// no needs_wildcard marker
|
||||
false
|
||||
};
|
||||
Ok(EnumConvertAttribute {
|
||||
path: paths[0].clone(),
|
||||
needs_wildcard,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn gen_enum_convert(attr: EnumConvertAttribute, input: ItemEnum) -> proc_macro2::TokenStream {
|
||||
let mut from_arms = vec![];
|
||||
let mut into_arms = vec![];
|
||||
|
||||
let this_name = &input.ident;
|
||||
let other_name = &attr.path;
|
||||
for variant in &input.variants {
|
||||
if variant.fields != Fields::Unit {
|
||||
return Error::new(variant.ident.span(), "variant may not have fields")
|
||||
.to_compile_error();
|
||||
}
|
||||
|
||||
let variant_name = &variant.ident;
|
||||
from_arms.push(quote!(#other_name::#variant_name => Self::#variant_name));
|
||||
into_arms.push(quote!(#this_name::#variant_name => Self::#variant_name));
|
||||
}
|
||||
|
||||
if attr.needs_wildcard {
|
||||
let error = format!(
|
||||
"Encountered unknown field for {}",
|
||||
other_name.to_token_stream()
|
||||
);
|
||||
from_arms.push(quote!(_ => unreachable!(#error)))
|
||||
}
|
||||
quote! {
|
||||
impl From<#other_name> for #this_name {
|
||||
fn from(other: #other_name) -> Self {
|
||||
match other {
|
||||
#(#from_arms,)*
|
||||
}
|
||||
}
|
||||
}
|
||||
impl From<#this_name> for #other_name {
|
||||
fn from(this: #this_name) -> Self {
|
||||
match this {
|
||||
#(#into_arms,)*
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
686
third_party/rust/diplomat/src/lib.rs
vendored
Normal file
686
third_party/rust/diplomat/src/lib.rs
vendored
Normal file
@ -0,0 +1,686 @@
|
||||
use proc_macro2::Span;
|
||||
use quote::{quote, ToTokens};
|
||||
use syn::*;
|
||||
|
||||
use diplomat_core::ast;
|
||||
|
||||
mod enum_convert;
|
||||
mod transparent_convert;
|
||||
|
||||
fn cfgs_to_stream(attrs: &[String]) -> proc_macro2::TokenStream {
|
||||
attrs.iter().fold(quote!(), |prev, attr| {
|
||||
let attr = attr.parse::<proc_macro2::TokenStream>().unwrap();
|
||||
quote!(#prev #attr)
|
||||
})
|
||||
}
|
||||
|
||||
fn gen_params_at_boundary(param: &ast::Param, expanded_params: &mut Vec<FnArg>) {
|
||||
match ¶m.ty {
|
||||
ast::TypeName::StrReference(_) | ast::TypeName::PrimitiveSlice(..) => {
|
||||
let data_type = if let ast::TypeName::PrimitiveSlice(.., prim) = ¶m.ty {
|
||||
ast::TypeName::Primitive(*prim).to_syn().to_token_stream()
|
||||
} else {
|
||||
quote! { u8 }
|
||||
};
|
||||
expanded_params.push(FnArg::Typed(PatType {
|
||||
attrs: vec![],
|
||||
pat: Box::new(Pat::Ident(PatIdent {
|
||||
attrs: vec![],
|
||||
by_ref: None,
|
||||
mutability: None,
|
||||
ident: Ident::new(&format!("{}_diplomat_data", param.name), Span::call_site()),
|
||||
subpat: None,
|
||||
})),
|
||||
colon_token: syn::token::Colon(Span::call_site()),
|
||||
ty: Box::new(
|
||||
parse2({
|
||||
if let ast::TypeName::PrimitiveSlice(_, ast::Mutability::Mutable, _) =
|
||||
¶m.ty
|
||||
{
|
||||
quote! { *mut #data_type }
|
||||
} else {
|
||||
quote! { *const #data_type }
|
||||
}
|
||||
})
|
||||
.unwrap(),
|
||||
),
|
||||
}));
|
||||
|
||||
expanded_params.push(FnArg::Typed(PatType {
|
||||
attrs: vec![],
|
||||
pat: Box::new(Pat::Ident(PatIdent {
|
||||
attrs: vec![],
|
||||
by_ref: None,
|
||||
mutability: None,
|
||||
ident: Ident::new(&format!("{}_diplomat_len", param.name), Span::call_site()),
|
||||
subpat: None,
|
||||
})),
|
||||
colon_token: syn::token::Colon(Span::call_site()),
|
||||
ty: Box::new(
|
||||
parse2(quote! {
|
||||
usize
|
||||
})
|
||||
.unwrap(),
|
||||
),
|
||||
}));
|
||||
}
|
||||
o => {
|
||||
expanded_params.push(FnArg::Typed(PatType {
|
||||
attrs: vec![],
|
||||
pat: Box::new(Pat::Ident(PatIdent {
|
||||
attrs: vec![],
|
||||
by_ref: None,
|
||||
mutability: None,
|
||||
ident: Ident::new(param.name.as_str(), Span::call_site()),
|
||||
subpat: None,
|
||||
})),
|
||||
colon_token: syn::token::Colon(Span::call_site()),
|
||||
ty: Box::new(o.to_syn()),
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_params_invocation(param: &ast::Param, expanded_params: &mut Vec<Expr>) {
|
||||
match ¶m.ty {
|
||||
ast::TypeName::StrReference(_) | ast::TypeName::PrimitiveSlice(..) => {
|
||||
let data_ident =
|
||||
Ident::new(&format!("{}_diplomat_data", param.name), Span::call_site());
|
||||
let len_ident = Ident::new(&format!("{}_diplomat_len", param.name), Span::call_site());
|
||||
|
||||
let tokens = if let ast::TypeName::PrimitiveSlice(_, mutability, _) = ¶m.ty {
|
||||
match mutability {
|
||||
ast::Mutability::Mutable => quote! {
|
||||
unsafe { core::slice::from_raw_parts_mut(#data_ident, #len_ident) }
|
||||
},
|
||||
ast::Mutability::Immutable => quote! {
|
||||
unsafe { core::slice::from_raw_parts(#data_ident, #len_ident) }
|
||||
},
|
||||
}
|
||||
} else {
|
||||
// TODO(#57): don't just unwrap? or should we assume that the other side gives us a good value?
|
||||
quote! {
|
||||
unsafe {
|
||||
core::str::from_utf8(core::slice::from_raw_parts(#data_ident, #len_ident)).unwrap()
|
||||
}
|
||||
}
|
||||
};
|
||||
expanded_params.push(parse2(tokens).unwrap());
|
||||
}
|
||||
ast::TypeName::Result(_, _, _) => {
|
||||
let param = ¶m.name;
|
||||
expanded_params.push(parse2(quote!(#param.into())).unwrap());
|
||||
}
|
||||
_ => {
|
||||
expanded_params.push(Expr::Path(ExprPath {
|
||||
attrs: vec![],
|
||||
qself: None,
|
||||
path: Ident::new(param.name.as_str(), Span::call_site()).into(),
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_custom_type_method(strct: &ast::CustomType, m: &ast::Method) -> Item {
|
||||
let self_ident = Ident::new(strct.name().as_str(), Span::call_site());
|
||||
let method_ident = Ident::new(m.name.as_str(), Span::call_site());
|
||||
let extern_ident = Ident::new(m.full_path_name.as_str(), Span::call_site());
|
||||
|
||||
let mut all_params = vec![];
|
||||
m.params.iter().for_each(|p| {
|
||||
gen_params_at_boundary(p, &mut all_params);
|
||||
});
|
||||
|
||||
let mut all_params_invocation = vec![];
|
||||
m.params.iter().for_each(|p| {
|
||||
gen_params_invocation(p, &mut all_params_invocation);
|
||||
});
|
||||
|
||||
let this_ident = Pat::Ident(PatIdent {
|
||||
attrs: vec![],
|
||||
by_ref: None,
|
||||
mutability: None,
|
||||
ident: Ident::new("this", Span::call_site()),
|
||||
subpat: None,
|
||||
});
|
||||
|
||||
if let Some(self_param) = &m.self_param {
|
||||
all_params.insert(
|
||||
0,
|
||||
FnArg::Typed(PatType {
|
||||
attrs: vec![],
|
||||
pat: Box::new(this_ident.clone()),
|
||||
colon_token: syn::token::Colon(Span::call_site()),
|
||||
ty: Box::new(self_param.to_typename().to_syn()),
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
let lifetimes = {
|
||||
let lifetime_env = &m.lifetime_env;
|
||||
if lifetime_env.is_empty() {
|
||||
quote! {}
|
||||
} else {
|
||||
quote! { <#lifetime_env> }
|
||||
}
|
||||
};
|
||||
|
||||
let method_invocation = if m.self_param.is_some() {
|
||||
quote! { #this_ident.#method_ident }
|
||||
} else {
|
||||
quote! { #self_ident::#method_ident }
|
||||
};
|
||||
|
||||
let (return_tokens, maybe_into) = if let Some(return_type) = &m.return_type {
|
||||
if let ast::TypeName::Result(ok, err, true) = return_type {
|
||||
let ok = ok.to_syn();
|
||||
let err = err.to_syn();
|
||||
(
|
||||
quote! { -> diplomat_runtime::DiplomatResult<#ok, #err> },
|
||||
quote! { .into() },
|
||||
)
|
||||
} else {
|
||||
let return_type_syn = return_type.to_syn();
|
||||
(quote! { -> #return_type_syn }, quote! {})
|
||||
}
|
||||
} else {
|
||||
(quote! {}, quote! {})
|
||||
};
|
||||
|
||||
let writeable_flushes = m
|
||||
.params
|
||||
.iter()
|
||||
.filter(|p| p.is_writeable())
|
||||
.map(|p| {
|
||||
let p = &p.name;
|
||||
quote! { #p.flush(); }
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let cfg = cfgs_to_stream(&m.cfg_attrs);
|
||||
|
||||
if writeable_flushes.is_empty() {
|
||||
Item::Fn(syn::parse_quote! {
|
||||
#[no_mangle]
|
||||
#cfg
|
||||
extern "C" fn #extern_ident#lifetimes(#(#all_params),*) #return_tokens {
|
||||
#method_invocation(#(#all_params_invocation),*) #maybe_into
|
||||
}
|
||||
})
|
||||
} else {
|
||||
Item::Fn(syn::parse_quote! {
|
||||
#[no_mangle]
|
||||
#cfg
|
||||
extern "C" fn #extern_ident#lifetimes(#(#all_params),*) #return_tokens {
|
||||
let ret = #method_invocation(#(#all_params_invocation),*);
|
||||
#(#writeable_flushes)*
|
||||
ret #maybe_into
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct AttributeInfo {
|
||||
repr: bool,
|
||||
opaque: bool,
|
||||
}
|
||||
|
||||
impl AttributeInfo {
|
||||
fn extract(attrs: &mut Vec<Attribute>) -> Self {
|
||||
let mut repr = false;
|
||||
let mut opaque = false;
|
||||
attrs.retain(|attr| {
|
||||
let ident = &attr.path().segments.iter().next().unwrap().ident;
|
||||
if ident == "repr" {
|
||||
repr = true;
|
||||
// don't actually extract repr attrs, just detect them
|
||||
return true;
|
||||
} else if ident == "diplomat" {
|
||||
if attr.path().segments.len() == 2 {
|
||||
let seg = &attr.path().segments.iter().nth(1).unwrap().ident;
|
||||
if seg == "opaque" {
|
||||
opaque = true;
|
||||
return false;
|
||||
} else if seg == "rust_link" || seg == "out" {
|
||||
// diplomat-tool reads these, not diplomat::bridge.
|
||||
// throw them away so rustc doesn't complain about unknown attributes
|
||||
return false;
|
||||
} else if seg == "enum_convert" || seg == "transparent_convert" {
|
||||
// diplomat::bridge doesn't read this, but it's handled separately
|
||||
// as an attribute
|
||||
return true;
|
||||
} else {
|
||||
panic!("Only #[diplomat::opaque] and #[diplomat::rust_link] are supported")
|
||||
}
|
||||
} else {
|
||||
panic!("#[diplomat::foo] attrs have a single-segment path name")
|
||||
}
|
||||
}
|
||||
true
|
||||
});
|
||||
|
||||
Self { repr, opaque }
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_bridge(input: ItemMod) -> ItemMod {
|
||||
let module = ast::Module::from_syn(&input, true);
|
||||
let (brace, mut new_contents) = input.content.unwrap();
|
||||
|
||||
new_contents.iter_mut().for_each(|c| match c {
|
||||
Item::Struct(s) => {
|
||||
let info = AttributeInfo::extract(&mut s.attrs);
|
||||
if info.opaque || !info.repr {
|
||||
let repr = if info.opaque {
|
||||
// Normal opaque types don't need repr(transparent) because the inner type is
|
||||
// never referenced. #[diplomat::transparent_convert] handles adding repr(transparent)
|
||||
// on its own
|
||||
quote!()
|
||||
} else {
|
||||
quote!(#[repr(C)])
|
||||
};
|
||||
*s = syn::parse_quote! {
|
||||
#repr
|
||||
#s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item::Enum(e) => {
|
||||
let info = AttributeInfo::extract(&mut e.attrs);
|
||||
if info.opaque {
|
||||
panic!("#[diplomat::opaque] not allowed on enums")
|
||||
}
|
||||
*e = syn::parse_quote! {
|
||||
#[repr(C)]
|
||||
#e
|
||||
};
|
||||
}
|
||||
|
||||
Item::Impl(i) => {
|
||||
for item in &mut i.items {
|
||||
if let syn::ImplItem::Fn(ref mut m) = *item {
|
||||
let info = AttributeInfo::extract(&mut m.attrs);
|
||||
if info.opaque {
|
||||
panic!("#[diplomat::opaque] not allowed on methods")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
});
|
||||
|
||||
for custom_type in module.declared_types.values() {
|
||||
custom_type.methods().iter().for_each(|m| {
|
||||
new_contents.push(gen_custom_type_method(custom_type, m));
|
||||
});
|
||||
|
||||
let destroy_ident = Ident::new(
|
||||
format!("{}_destroy", custom_type.name()).as_str(),
|
||||
Span::call_site(),
|
||||
);
|
||||
|
||||
let type_ident = custom_type.name().to_syn();
|
||||
|
||||
let (lifetime_defs, lifetimes) = if let Some(lifetime_env) = custom_type.lifetimes() {
|
||||
(
|
||||
quote! { <#lifetime_env> },
|
||||
lifetime_env.lifetimes_to_tokens(),
|
||||
)
|
||||
} else {
|
||||
(quote! {}, quote! {})
|
||||
};
|
||||
|
||||
let cfg = cfgs_to_stream(custom_type.cfg_attrs());
|
||||
|
||||
// for now, body is empty since all we need to do is drop the box
|
||||
// TODO(#13): change to take a `*mut` and handle DST boxes appropriately
|
||||
new_contents.push(Item::Fn(syn::parse_quote! {
|
||||
#[no_mangle]
|
||||
#cfg
|
||||
extern "C" fn #destroy_ident#lifetime_defs(this: Box<#type_ident#lifetimes>) {}
|
||||
}));
|
||||
}
|
||||
|
||||
ItemMod {
|
||||
attrs: input.attrs,
|
||||
vis: input.vis,
|
||||
mod_token: input.mod_token,
|
||||
ident: input.ident,
|
||||
content: Some((brace, new_contents)),
|
||||
semi: input.semi,
|
||||
unsafety: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Mark a module to be exposed through Diplomat-generated FFI.
|
||||
#[proc_macro_attribute]
|
||||
pub fn bridge(
|
||||
_attr: proc_macro::TokenStream,
|
||||
input: proc_macro::TokenStream,
|
||||
) -> proc_macro::TokenStream {
|
||||
let expanded = gen_bridge(parse_macro_input!(input));
|
||||
proc_macro::TokenStream::from(expanded.to_token_stream())
|
||||
}
|
||||
|
||||
/// Generate From and Into implementations for a Diplomat enum
|
||||
///
|
||||
/// This is invoked as `#[diplomat::enum_convert(OtherEnumName)]`
|
||||
/// on a Diplomat enum. It will assume the other enum has exactly the same variants
|
||||
/// and generate From and Into implementations using those. In case that enum is `#[non_exhaustive]`,
|
||||
/// you may use `#[diplomat::enum_convert(OtherEnumName, needs_wildcard)]` to generate a panicky wildcard
|
||||
/// branch. It is up to the library author to ensure the enums are kept in sync. You may use the `#[non_exhaustive_omitted_patterns]`
|
||||
/// lint to enforce this.
|
||||
#[proc_macro_attribute]
|
||||
pub fn enum_convert(
|
||||
attr: proc_macro::TokenStream,
|
||||
input: proc_macro::TokenStream,
|
||||
) -> proc_macro::TokenStream {
|
||||
// proc macros handle compile errors by using special error tokens.
|
||||
// In case of an error, we don't want the original code to go away too
|
||||
// (otherwise that will cause more errors) so we hold on to it and we tack it in
|
||||
// with no modifications below
|
||||
let input_cached: proc_macro2::TokenStream = input.clone().into();
|
||||
let expanded =
|
||||
enum_convert::gen_enum_convert(parse_macro_input!(attr), parse_macro_input!(input));
|
||||
|
||||
let full = quote! {
|
||||
#expanded
|
||||
#input_cached
|
||||
};
|
||||
proc_macro::TokenStream::from(full.to_token_stream())
|
||||
}
|
||||
|
||||
/// Generate conversions from inner types for opaque Diplomat types with a single field
|
||||
///
|
||||
/// This is invoked as `#[diplomat::transparent_convert]`
|
||||
/// on an opaque Diplomat type. It will add `#[repr(transparent)]` and implement `pub(crate) fn transparent_convert()`
|
||||
/// which allows constructing an `&Self` from a reference to the inner field.
|
||||
#[proc_macro_attribute]
|
||||
pub fn transparent_convert(
|
||||
_attr: proc_macro::TokenStream,
|
||||
input: proc_macro::TokenStream,
|
||||
) -> proc_macro::TokenStream {
|
||||
// proc macros handle compile errors by using special error tokens.
|
||||
// In case of an error, we don't want the original code to go away too
|
||||
// (otherwise that will cause more errors) so we hold on to it and we tack it in
|
||||
// with no modifications below
|
||||
let input_cached: proc_macro2::TokenStream = input.clone().into();
|
||||
let expanded = transparent_convert::gen_transparent_convert(parse_macro_input!(input));
|
||||
|
||||
let full = quote! {
|
||||
#expanded
|
||||
#input_cached
|
||||
};
|
||||
proc_macro::TokenStream::from(full.to_token_stream())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
use std::process::Command;
|
||||
|
||||
use quote::ToTokens;
|
||||
use syn::parse_quote;
|
||||
use tempfile::tempdir;
|
||||
|
||||
use super::gen_bridge;
|
||||
|
||||
fn rustfmt_code(code: &str) -> String {
|
||||
let dir = tempdir().unwrap();
|
||||
let file_path = dir.path().join("temp.rs");
|
||||
let mut file = File::create(file_path.clone()).unwrap();
|
||||
|
||||
writeln!(file, "{code}").unwrap();
|
||||
drop(file);
|
||||
|
||||
Command::new("rustfmt")
|
||||
.arg(file_path.to_str().unwrap())
|
||||
.spawn()
|
||||
.unwrap()
|
||||
.wait()
|
||||
.unwrap();
|
||||
|
||||
let mut file = File::open(file_path).unwrap();
|
||||
let mut data = String::new();
|
||||
file.read_to_string(&mut data).unwrap();
|
||||
drop(file);
|
||||
dir.close().unwrap();
|
||||
data
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn method_taking_str() {
|
||||
insta::assert_display_snapshot!(rustfmt_code(
|
||||
&gen_bridge(parse_quote! {
|
||||
mod ffi {
|
||||
struct Foo {}
|
||||
|
||||
impl Foo {
|
||||
pub fn from_str(s: &str) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.to_token_stream()
|
||||
.to_string()
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn method_taking_slice() {
|
||||
insta::assert_display_snapshot!(rustfmt_code(
|
||||
&gen_bridge(parse_quote! {
|
||||
mod ffi {
|
||||
struct Foo {}
|
||||
|
||||
impl Foo {
|
||||
pub fn from_slice(s: &[f64]) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.to_token_stream()
|
||||
.to_string()
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn method_taking_mutable_slice() {
|
||||
insta::assert_display_snapshot!(rustfmt_code(
|
||||
&gen_bridge(parse_quote! {
|
||||
mod ffi {
|
||||
struct Foo {}
|
||||
|
||||
impl Foo {
|
||||
pub fn fill_slice(s: &mut [f64]) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.to_token_stream()
|
||||
.to_string()
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mod_with_enum() {
|
||||
insta::assert_display_snapshot!(rustfmt_code(
|
||||
&gen_bridge(parse_quote! {
|
||||
mod ffi {
|
||||
enum Abc {
|
||||
A,
|
||||
B = 123,
|
||||
}
|
||||
|
||||
impl Abc {
|
||||
pub fn do_something(&self) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.to_token_stream()
|
||||
.to_string()
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mod_with_writeable_result() {
|
||||
insta::assert_display_snapshot!(rustfmt_code(
|
||||
&gen_bridge(parse_quote! {
|
||||
mod ffi {
|
||||
struct Foo {}
|
||||
|
||||
impl Foo {
|
||||
pub fn to_string(&self, to: &mut DiplomatWriteable) -> Result<(), ()> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.to_token_stream()
|
||||
.to_string()
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mod_with_rust_result() {
|
||||
insta::assert_display_snapshot!(rustfmt_code(
|
||||
&gen_bridge(parse_quote! {
|
||||
mod ffi {
|
||||
struct Foo {}
|
||||
|
||||
impl Foo {
|
||||
pub fn bar(&self) -> Result<(), ()> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.to_token_stream()
|
||||
.to_string()
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multilevel_borrows() {
|
||||
insta::assert_display_snapshot!(rustfmt_code(
|
||||
&gen_bridge(parse_quote! {
|
||||
mod ffi {
|
||||
#[diplomat::opaque]
|
||||
struct Foo<'a>(&'a str);
|
||||
|
||||
#[diplomat::opaque]
|
||||
struct Bar<'b, 'a: 'b>(&'b Foo<'a>);
|
||||
|
||||
struct Baz<'x, 'y> {
|
||||
foo: &'y Foo<'x>,
|
||||
}
|
||||
|
||||
impl<'a> Foo<'a> {
|
||||
pub fn new(x: &'a str) -> Box<Foo<'a>> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn get_bar<'b>(&'b self) -> Box<Bar<'b, 'a>> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn get_baz<'b>(&'b self) -> Baz<'b, 'a> {
|
||||
Bax { foo: self }
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.to_token_stream()
|
||||
.to_string()
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn self_params() {
|
||||
insta::assert_display_snapshot!(rustfmt_code(
|
||||
&gen_bridge(parse_quote! {
|
||||
mod ffi {
|
||||
#[diplomat::opaque]
|
||||
struct RefList<'a> {
|
||||
data: &'a i32,
|
||||
next: Option<Box<Self>>,
|
||||
}
|
||||
|
||||
impl<'b> RefList<'b> {
|
||||
pub fn extend(&mut self, other: &Self) -> Self {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.to_token_stream()
|
||||
.to_string()
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cfged_method() {
|
||||
insta::assert_display_snapshot!(rustfmt_code(
|
||||
&gen_bridge(parse_quote! {
|
||||
mod ffi {
|
||||
struct Foo {}
|
||||
|
||||
impl Foo {
|
||||
#[cfg(feature = "foo")]
|
||||
pub fn bar(s: u8) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.to_token_stream()
|
||||
.to_string()
|
||||
));
|
||||
|
||||
insta::assert_display_snapshot!(rustfmt_code(
|
||||
&gen_bridge(parse_quote! {
|
||||
mod ffi {
|
||||
struct Foo {}
|
||||
|
||||
#[cfg(feature = "bar")]
|
||||
impl Foo {
|
||||
#[cfg(feature = "foo")]
|
||||
pub fn bar(s: u8) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.to_token_stream()
|
||||
.to_string()
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cfgd_struct() {
|
||||
insta::assert_display_snapshot!(rustfmt_code(
|
||||
&gen_bridge(parse_quote! {
|
||||
mod ffi {
|
||||
#[diplomat::opaque]
|
||||
#[cfg(feature = "foo")]
|
||||
struct Foo {}
|
||||
#[cfg(feature = "foo")]
|
||||
impl Foo {
|
||||
pub fn bar(s: u8) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.to_token_stream()
|
||||
.to_string()
|
||||
));
|
||||
}
|
||||
}
|
23
third_party/rust/diplomat/src/snapshots/diplomat__tests__cfgd_struct.snap
vendored
Normal file
23
third_party/rust/diplomat/src/snapshots/diplomat__tests__cfgd_struct.snap
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
---
|
||||
source: macro/src/lib.rs
|
||||
expression: "rustfmt_code(&gen_bridge(parse_quote! {\n mod ffi\n {\n #[diplomat :: opaque] #[cfg(feature = \"foo\")] struct Foo {}\n #[cfg(feature = \"foo\")] impl Foo\n { pub fn bar(s : u8) { unimplemented! () } }\n }\n }).to_token_stream().to_string())"
|
||||
---
|
||||
mod ffi {
|
||||
#[cfg(feature = "foo")]
|
||||
struct Foo {}
|
||||
#[cfg(feature = "foo")]
|
||||
impl Foo {
|
||||
pub fn bar(s: u8) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
#[no_mangle]
|
||||
#[cfg(feature = "foo")]
|
||||
extern "C" fn Foo_bar(s: u8) {
|
||||
Foo::bar(s)
|
||||
}
|
||||
#[no_mangle]
|
||||
#[cfg(feature = "foo")]
|
||||
extern "C" fn Foo_destroy(this: Box<Foo>) {}
|
||||
}
|
||||
|
24
third_party/rust/diplomat/src/snapshots/diplomat__tests__cfged_method-2.snap
vendored
Normal file
24
third_party/rust/diplomat/src/snapshots/diplomat__tests__cfged_method-2.snap
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
---
|
||||
source: macro/src/lib.rs
|
||||
expression: "rustfmt_code(&gen_bridge(parse_quote! {\n mod ffi\n {\n struct Foo {} #[cfg(feature = \"bar\")] impl Foo\n {\n #[cfg(feature = \"foo\")] pub fn bar(s : u8)\n { unimplemented! () }\n }\n }\n }).to_token_stream().to_string())"
|
||||
---
|
||||
mod ffi {
|
||||
#[repr(C)]
|
||||
struct Foo {}
|
||||
#[cfg(feature = "bar")]
|
||||
impl Foo {
|
||||
#[cfg(feature = "foo")]
|
||||
pub fn bar(s: u8) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
#[no_mangle]
|
||||
#[cfg(feature = "bar")]
|
||||
#[cfg(feature = "foo")]
|
||||
extern "C" fn Foo_bar(s: u8) {
|
||||
Foo::bar(s)
|
||||
}
|
||||
#[no_mangle]
|
||||
extern "C" fn Foo_destroy(this: Box<Foo>) {}
|
||||
}
|
||||
|
22
third_party/rust/diplomat/src/snapshots/diplomat__tests__cfged_method.snap
vendored
Normal file
22
third_party/rust/diplomat/src/snapshots/diplomat__tests__cfged_method.snap
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
---
|
||||
source: macro/src/lib.rs
|
||||
expression: "rustfmt_code(&gen_bridge(parse_quote! {\n mod ffi\n {\n struct Foo {} impl Foo\n {\n #[cfg(feature = \"foo\")] pub fn bar(s : u8)\n { unimplemented! () }\n }\n }\n }).to_token_stream().to_string())"
|
||||
---
|
||||
mod ffi {
|
||||
#[repr(C)]
|
||||
struct Foo {}
|
||||
impl Foo {
|
||||
#[cfg(feature = "foo")]
|
||||
pub fn bar(s: u8) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
#[no_mangle]
|
||||
#[cfg(feature = "foo")]
|
||||
extern "C" fn Foo_bar(s: u8) {
|
||||
Foo::bar(s)
|
||||
}
|
||||
#[no_mangle]
|
||||
extern "C" fn Foo_destroy(this: Box<Foo>) {}
|
||||
}
|
||||
|
20
third_party/rust/diplomat/src/snapshots/diplomat__tests__method_taking_mutable_slice.snap
vendored
Normal file
20
third_party/rust/diplomat/src/snapshots/diplomat__tests__method_taking_mutable_slice.snap
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
---
|
||||
source: macro/src/lib.rs
|
||||
expression: "rustfmt_code(&gen_bridge(parse_quote! {\n mod ffi\n {\n struct Foo {} impl Foo\n { pub fn fill_slice(s : & mut [f64]) { unimplemented! () } }\n }\n }).to_token_stream().to_string())"
|
||||
---
|
||||
mod ffi {
|
||||
#[repr(C)]
|
||||
struct Foo {}
|
||||
impl Foo {
|
||||
pub fn fill_slice(s: &mut [f64]) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
#[no_mangle]
|
||||
extern "C" fn Foo_fill_slice(s_diplomat_data: *mut f64, s_diplomat_len: usize) {
|
||||
Foo::fill_slice(unsafe { core::slice::from_raw_parts_mut(s_diplomat_data, s_diplomat_len) })
|
||||
}
|
||||
#[no_mangle]
|
||||
extern "C" fn Foo_destroy(this: Box<Foo>) {}
|
||||
}
|
||||
|
26
third_party/rust/diplomat/src/snapshots/diplomat__tests__method_taking_mutable_str.snap
vendored
Normal file
26
third_party/rust/diplomat/src/snapshots/diplomat__tests__method_taking_mutable_str.snap
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
---
|
||||
source: macro/src/lib.rs
|
||||
expression: "rustfmt_code(&gen_bridge(parse_quote! {\n mod ffi\n {\n struct Foo {} impl Foo\n {\n pub fn make_uppercase(s : & mut str) { unimplemented! () }\n }\n }\n }).to_token_stream().to_string())"
|
||||
---
|
||||
mod ffi {
|
||||
#[repr(C)]
|
||||
struct Foo {}
|
||||
impl Foo {
|
||||
pub fn make_uppercase(s: &mut str) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
#[no_mangle]
|
||||
extern "C" fn Foo_make_uppercase(s_diplomat_data: *mut u8, s_diplomat_len: usize) {
|
||||
Foo::make_uppercase(unsafe {
|
||||
core::str::from_utf8_mut(core::slice::from_raw_parts_mut(
|
||||
s_diplomat_data,
|
||||
s_diplomat_len,
|
||||
))
|
||||
.unwrap()
|
||||
})
|
||||
}
|
||||
#[no_mangle]
|
||||
extern "C" fn Foo_destroy(this: Box<Foo>) {}
|
||||
}
|
||||
|
20
third_party/rust/diplomat/src/snapshots/diplomat__tests__method_taking_slice.snap
vendored
Normal file
20
third_party/rust/diplomat/src/snapshots/diplomat__tests__method_taking_slice.snap
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
---
|
||||
source: macro/src/lib.rs
|
||||
expression: "rustfmt_code(&gen_bridge(parse_quote! {\n mod ffi\n {\n struct Foo {} impl Foo\n { pub fn from_slice(s : & [f64]) { unimplemented! () } }\n }\n }).to_token_stream().to_string())"
|
||||
---
|
||||
mod ffi {
|
||||
#[repr(C)]
|
||||
struct Foo {}
|
||||
impl Foo {
|
||||
pub fn from_slice(s: &[f64]) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
#[no_mangle]
|
||||
extern "C" fn Foo_from_slice(s_diplomat_data: *const f64, s_diplomat_len: usize) {
|
||||
Foo::from_slice(unsafe { core::slice::from_raw_parts(s_diplomat_data, s_diplomat_len) })
|
||||
}
|
||||
#[no_mangle]
|
||||
extern "C" fn Foo_destroy(this: Box<Foo>) {}
|
||||
}
|
||||
|
23
third_party/rust/diplomat/src/snapshots/diplomat__tests__method_taking_str.snap
vendored
Normal file
23
third_party/rust/diplomat/src/snapshots/diplomat__tests__method_taking_str.snap
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
---
|
||||
source: macro/src/lib.rs
|
||||
expression: "rustfmt_code(&gen_bridge(parse_quote! {\n mod ffi\n {\n struct Foo {} impl Foo\n { pub fn from_str(s : & str) { unimplemented! () } }\n }\n }).to_token_stream().to_string())"
|
||||
---
|
||||
mod ffi {
|
||||
#[repr(C)]
|
||||
struct Foo {}
|
||||
impl Foo {
|
||||
pub fn from_str(s: &str) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
#[no_mangle]
|
||||
extern "C" fn Foo_from_str(s_diplomat_data: *const u8, s_diplomat_len: usize) {
|
||||
Foo::from_str(unsafe {
|
||||
core::str::from_utf8(core::slice::from_raw_parts(s_diplomat_data, s_diplomat_len))
|
||||
.unwrap()
|
||||
})
|
||||
}
|
||||
#[no_mangle]
|
||||
extern "C" fn Foo_destroy(this: Box<Foo>) {}
|
||||
}
|
||||
|
23
third_party/rust/diplomat/src/snapshots/diplomat__tests__mod_with_enum.snap
vendored
Normal file
23
third_party/rust/diplomat/src/snapshots/diplomat__tests__mod_with_enum.snap
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
---
|
||||
source: macro/src/lib.rs
|
||||
expression: "rustfmt_code(&gen_bridge(parse_quote! {\n mod ffi\n {\n enum Abc { A, B = 123, } impl Abc\n { pub fn do_something(& self) { unimplemented! () } }\n }\n }).to_token_stream().to_string())"
|
||||
---
|
||||
mod ffi {
|
||||
#[repr(C)]
|
||||
enum Abc {
|
||||
A,
|
||||
B = 123,
|
||||
}
|
||||
impl Abc {
|
||||
pub fn do_something(&self) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
#[no_mangle]
|
||||
extern "C" fn Abc_do_something(this: &Abc) {
|
||||
this.do_something()
|
||||
}
|
||||
#[no_mangle]
|
||||
extern "C" fn Abc_destroy(this: Box<Abc>) {}
|
||||
}
|
||||
|
20
third_party/rust/diplomat/src/snapshots/diplomat__tests__mod_with_rust_result.snap
vendored
Normal file
20
third_party/rust/diplomat/src/snapshots/diplomat__tests__mod_with_rust_result.snap
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
---
|
||||
source: macro/src/lib.rs
|
||||
expression: "rustfmt_code(&gen_bridge(parse_quote! {\n mod ffi\n {\n struct Foo {} impl Foo\n {\n pub fn bar(& self) -> Result < (), () >\n { unimplemented! () }\n }\n }\n }).to_token_stream().to_string())"
|
||||
---
|
||||
mod ffi {
|
||||
#[repr(C)]
|
||||
struct Foo {}
|
||||
impl Foo {
|
||||
pub fn bar(&self) -> Result<(), ()> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
#[no_mangle]
|
||||
extern "C" fn Foo_bar(this: &Foo) -> diplomat_runtime::DiplomatResult<(), ()> {
|
||||
this.bar().into()
|
||||
}
|
||||
#[no_mangle]
|
||||
extern "C" fn Foo_destroy(this: Box<Foo>) {}
|
||||
}
|
||||
|
25
third_party/rust/diplomat/src/snapshots/diplomat__tests__mod_with_writeable_result.snap
vendored
Normal file
25
third_party/rust/diplomat/src/snapshots/diplomat__tests__mod_with_writeable_result.snap
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
---
|
||||
source: macro/src/lib.rs
|
||||
expression: "rustfmt_code(&gen_bridge(parse_quote! {\n mod ffi\n {\n struct Foo {} impl Foo\n {\n pub fn to_string(& self, to : & mut DiplomatWriteable) ->\n Result < (), () > { unimplemented! () }\n }\n }\n }).to_token_stream().to_string())"
|
||||
---
|
||||
mod ffi {
|
||||
#[repr(C)]
|
||||
struct Foo {}
|
||||
impl Foo {
|
||||
pub fn to_string(&self, to: &mut DiplomatWriteable) -> Result<(), ()> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
#[no_mangle]
|
||||
extern "C" fn Foo_to_string(
|
||||
this: &Foo,
|
||||
to: &mut diplomat_runtime::DiplomatWriteable,
|
||||
) -> diplomat_runtime::DiplomatResult<(), ()> {
|
||||
let ret = this.to_string(to);
|
||||
to.flush();
|
||||
ret.into()
|
||||
}
|
||||
#[no_mangle]
|
||||
extern "C" fn Foo_destroy(this: Box<Foo>) {}
|
||||
}
|
||||
|
45
third_party/rust/diplomat/src/snapshots/diplomat__tests__multilevel_borrows.snap
vendored
Normal file
45
third_party/rust/diplomat/src/snapshots/diplomat__tests__multilevel_borrows.snap
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
---
|
||||
source: macro/src/lib.rs
|
||||
expression: "rustfmt_code(&gen_bridge(parse_quote! {\n mod ffi\n {\n #[diplomat :: opaque] struct Foo < 'a > (& 'a str) ;\n #[diplomat :: opaque] struct Bar < 'b, 'a : 'b >\n (& 'b Foo < 'a >) ; struct Baz < 'x, 'y >\n { foo : & 'y Foo < 'x >, } impl < 'a > Foo < 'a >\n {\n pub fn new(x : & 'a str) -> Box < Foo < 'a >>\n { unimplemented! () } pub fn get_bar < 'b > (& 'b self) ->\n Box < Bar < 'b, 'a >> { unimplemented! () } pub fn get_baz <\n 'b > (& 'b self) -> Baz < 'b, 'a > { Bax { foo : self } }\n }\n }\n }).to_token_stream().to_string())"
|
||||
---
|
||||
mod ffi {
|
||||
struct Foo<'a>(&'a str);
|
||||
struct Bar<'b, 'a: 'b>(&'b Foo<'a>);
|
||||
#[repr(C)]
|
||||
struct Baz<'x, 'y> {
|
||||
foo: &'y Foo<'x>,
|
||||
}
|
||||
impl<'a> Foo<'a> {
|
||||
pub fn new(x: &'a str) -> Box<Foo<'a>> {
|
||||
unimplemented!()
|
||||
}
|
||||
pub fn get_bar<'b>(&'b self) -> Box<Bar<'b, 'a>> {
|
||||
unimplemented!()
|
||||
}
|
||||
pub fn get_baz<'b>(&'b self) -> Baz<'b, 'a> {
|
||||
Bax { foo: self }
|
||||
}
|
||||
}
|
||||
#[no_mangle]
|
||||
extern "C" fn Bar_destroy<'b, 'a: 'b>(this: Box<Bar<'b, 'a>>) {}
|
||||
#[no_mangle]
|
||||
extern "C" fn Baz_destroy<'x: 'y, 'y>(this: Box<Baz<'x, 'y>>) {}
|
||||
#[no_mangle]
|
||||
extern "C" fn Foo_new<'a>(x_diplomat_data: *const u8, x_diplomat_len: usize) -> Box<Foo<'a>> {
|
||||
Foo::new(unsafe {
|
||||
core::str::from_utf8(core::slice::from_raw_parts(x_diplomat_data, x_diplomat_len))
|
||||
.unwrap()
|
||||
})
|
||||
}
|
||||
#[no_mangle]
|
||||
extern "C" fn Foo_get_bar<'a: 'b, 'b>(this: &'b Foo<'a>) -> Box<Bar<'b, 'a>> {
|
||||
this.get_bar()
|
||||
}
|
||||
#[no_mangle]
|
||||
extern "C" fn Foo_get_baz<'a: 'b, 'b>(this: &'b Foo<'a>) -> Baz<'b, 'a> {
|
||||
this.get_baz()
|
||||
}
|
||||
#[no_mangle]
|
||||
extern "C" fn Foo_destroy<'a>(this: Box<Foo<'a>>) {}
|
||||
}
|
||||
|
22
third_party/rust/diplomat/src/snapshots/diplomat__tests__self_params.snap
vendored
Normal file
22
third_party/rust/diplomat/src/snapshots/diplomat__tests__self_params.snap
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
---
|
||||
source: macro/src/lib.rs
|
||||
expression: "rustfmt_code(&gen_bridge(parse_quote! {\n mod ffi\n {\n #[diplomat :: opaque] struct RefList < 'a >\n { data : & 'a i32, next : Option < Box < Self >>, } impl <\n 'b > RefList < 'b >\n {\n pub fn extend(& mut self, other : & Self) -> Self\n { unimplemented! () }\n }\n }\n }).to_token_stream().to_string())"
|
||||
---
|
||||
mod ffi {
|
||||
struct RefList<'a> {
|
||||
data: &'a i32,
|
||||
next: Option<Box<Self>>,
|
||||
}
|
||||
impl<'b> RefList<'b> {
|
||||
pub fn extend(&mut self, other: &Self) -> Self {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
#[no_mangle]
|
||||
extern "C" fn RefList_extend<'b>(this: &mut RefList<'b>, other: &RefList<'b>) -> RefList<'b> {
|
||||
this.extend(other)
|
||||
}
|
||||
#[no_mangle]
|
||||
extern "C" fn RefList_destroy<'a>(this: Box<RefList<'a>>) {}
|
||||
}
|
||||
|
30
third_party/rust/diplomat/src/transparent_convert.rs
vendored
Normal file
30
third_party/rust/diplomat/src/transparent_convert.rs
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
use quote::quote;
|
||||
use syn::*;
|
||||
|
||||
pub fn gen_transparent_convert(s: ItemStruct) -> proc_macro2::TokenStream {
|
||||
let mut fields = s.fields.iter();
|
||||
let field1 = if let Some(field1) = fields.next() {
|
||||
&field1.ty
|
||||
} else {
|
||||
panic!("#[diplomat::transparent_convert] only allowed on structs with a single field")
|
||||
};
|
||||
|
||||
if fields.next().is_some() {
|
||||
panic!("#[diplomat::transparent_convert] only allowed on structs with a single field")
|
||||
}
|
||||
let struct_name = &s.ident;
|
||||
let (impl_generics, ty_generics, _) = s.generics.split_for_impl();
|
||||
let mut impl_generics: Generics = parse_quote!(#impl_generics);
|
||||
let custom_lifetime: GenericParam = parse_quote!('transparent_convert_outer);
|
||||
impl_generics.params.push(custom_lifetime);
|
||||
quote! {
|
||||
impl #impl_generics #struct_name #ty_generics {
|
||||
// can potentially add transparent_convert_owned, _mut later
|
||||
pub(crate) fn transparent_convert(from: &'transparent_convert_outer #field1) -> &'transparent_convert_outer Self {
|
||||
unsafe {
|
||||
&*(from as *const #field1 as *const Self)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
1
third_party/rust/diplomat_core/.cargo-checksum.json
vendored
Normal file
1
third_party/rust/diplomat_core/.cargo-checksum.json
vendored
Normal file
File diff suppressed because one or more lines are too long
66
third_party/rust/diplomat_core/Cargo.toml
vendored
Normal file
66
third_party/rust/diplomat_core/Cargo.toml
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
# 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"
|
||||
name = "diplomat_core"
|
||||
version = "0.5.2"
|
||||
authors = [
|
||||
"Shadaj Laddad <shadaj@users.noreply.github.com>",
|
||||
"Manish Goregaokar <manishsmail@gmail.com>",
|
||||
"Quinn Okabayashi <QnnOkabayashi@users.noreply.github.com>",
|
||||
]
|
||||
description = "Shared utilities between Diplomat macros and code generation"
|
||||
documentation = "https://docs.rs/diplomat_core/"
|
||||
keywords = [
|
||||
"ffi",
|
||||
"codegen",
|
||||
]
|
||||
categories = ["development-tools"]
|
||||
license = "MIT/Apache-2.0"
|
||||
repository = "https://github.com/rust-diplomat/diplomat"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
||||
[lib]
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
lazy_static = "1.4.0"
|
||||
proc-macro2 = "1.0.27"
|
||||
quote = "1.0"
|
||||
smallvec = "1.9.0"
|
||||
|
||||
[dependencies.displaydoc]
|
||||
version = "0.2"
|
||||
optional = true
|
||||
|
||||
[dependencies.serde]
|
||||
version = "1.0"
|
||||
features = ["derive"]
|
||||
|
||||
[dependencies.strck_ident]
|
||||
version = "0.1"
|
||||
features = ["rust"]
|
||||
|
||||
[dependencies.syn]
|
||||
version = "2"
|
||||
features = [
|
||||
"full",
|
||||
"extra-traits",
|
||||
]
|
||||
|
||||
[dev-dependencies]
|
||||
insta = "1.7.1"
|
||||
|
||||
[features]
|
||||
hir = []
|
201
third_party/rust/diplomat_core/LICENSE-APACHE
vendored
Normal file
201
third_party/rust/diplomat_core/LICENSE-APACHE
vendored
Normal file
@ -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 2022 The Diplomat Developers
|
||||
|
||||
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.
|
27
third_party/rust/diplomat_core/LICENSE-MIT
vendored
Normal file
27
third_party/rust/diplomat_core/LICENSE-MIT
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 The Diplomat 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.
|
10
third_party/rust/diplomat_core/src/ast/attrs.rs
vendored
Normal file
10
third_party/rust/diplomat_core/src/ast/attrs.rs
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
//! This module contains utilities for dealing with Rust attributes
|
||||
|
||||
use syn::Attribute;
|
||||
|
||||
pub(crate) fn extract_cfg_attrs(attrs: &[Attribute]) -> impl Iterator<Item = String> + '_ {
|
||||
attrs
|
||||
.iter()
|
||||
.filter(|&a| a.path().is_ident("cfg"))
|
||||
.map(|a| quote::quote!(#a).to_string())
|
||||
}
|
431
third_party/rust/diplomat_core/src/ast/docs.rs
vendored
Normal file
431
third_party/rust/diplomat_core/src/ast/docs.rs
vendored
Normal file
@ -0,0 +1,431 @@
|
||||
use super::Path;
|
||||
use core::fmt;
|
||||
use quote::ToTokens;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use syn::parse::{self, Parse, ParseStream};
|
||||
use syn::{Attribute, Ident, Meta, Token};
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Debug, Default)]
|
||||
pub struct Docs(String, Vec<RustLink>);
|
||||
|
||||
/// The type of markdown generated by [`Docs::to_markdown()`]
|
||||
///
|
||||
/// Note that this only controls markdown generated by this code. Existing markdown
|
||||
/// in the Rust documentation will not be sanitized in any way.
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
pub enum MarkdownStyle {
|
||||
/// Regular markdown with no specific extensions, compatible with most common flavors
|
||||
Normal,
|
||||
/// Markdown that can be losslessly converted to ReStructuredText
|
||||
RstCompat,
|
||||
}
|
||||
|
||||
impl Docs {
|
||||
pub fn from_attrs(attrs: &[Attribute]) -> Self {
|
||||
Self(Self::get_doc_lines(attrs), Self::get_rust_link(attrs))
|
||||
}
|
||||
|
||||
fn get_doc_lines(attrs: &[Attribute]) -> String {
|
||||
let mut lines: String = String::new();
|
||||
|
||||
attrs.iter().for_each(|attr| {
|
||||
if let Meta::NameValue(ref nv) = attr.meta {
|
||||
if nv.path.is_ident("doc") {
|
||||
let node: syn::LitStr = syn::parse2(nv.value.to_token_stream()).unwrap();
|
||||
let line = node.value().trim().to_string();
|
||||
|
||||
if !lines.is_empty() {
|
||||
lines.push('\n');
|
||||
}
|
||||
|
||||
lines.push_str(&line);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
lines
|
||||
}
|
||||
|
||||
fn get_rust_link(attrs: &[Attribute]) -> Vec<RustLink> {
|
||||
attrs
|
||||
.iter()
|
||||
.filter(|i| i.path().to_token_stream().to_string() == "diplomat :: rust_link")
|
||||
.map(|i| i.parse_args().expect("Malformed attribute"))
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.0.is_empty() && self.1.is_empty()
|
||||
}
|
||||
|
||||
/// Convert to markdown
|
||||
pub fn to_markdown(&self, docs_url_gen: &DocsUrlGenerator, style: MarkdownStyle) -> String {
|
||||
use std::fmt::Write;
|
||||
let mut lines = self.0.clone();
|
||||
let mut has_compact = false;
|
||||
let backtick = if style == MarkdownStyle::RstCompat {
|
||||
""
|
||||
} else {
|
||||
"`"
|
||||
};
|
||||
for rust_link in &self.1 {
|
||||
if rust_link.display == RustLinkDisplay::Compact {
|
||||
has_compact = true;
|
||||
} else if rust_link.display == RustLinkDisplay::Normal {
|
||||
write!(
|
||||
lines,
|
||||
"\n\nSee the [Rust documentation for {backtick}{name}{backtick}]({link}) for more information.",
|
||||
name = rust_link.path.elements.last().unwrap(),
|
||||
link = docs_url_gen.gen_for_rust_link(rust_link)
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
if has_compact {
|
||||
write!(lines, "\n\n Additional information: ").unwrap();
|
||||
for (i, rust_link) in self
|
||||
.1
|
||||
.iter()
|
||||
.filter(|r| r.display == RustLinkDisplay::Compact)
|
||||
.enumerate()
|
||||
{
|
||||
if i != 0 {
|
||||
write!(lines, ", ").unwrap();
|
||||
}
|
||||
write!(
|
||||
lines,
|
||||
"[{}]({})",
|
||||
i + 1,
|
||||
docs_url_gen.gen_for_rust_link(rust_link)
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
lines
|
||||
}
|
||||
|
||||
pub fn rust_links(&self) -> &[RustLink] {
|
||||
&self.1
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
pub enum RustLinkDisplay {
|
||||
/// A nice expanded representation that includes the type name
|
||||
///
|
||||
/// e.g. "See the \[link to Rust documentation\] for more details"
|
||||
Normal,
|
||||
/// A compact representation that will fit multiple rust_link entries in one line
|
||||
///
|
||||
/// E.g. "For further information, see: 1, 2, 3, 4" (all links)
|
||||
Compact,
|
||||
/// Hidden. Useful for programmatically annotating an API as related without showing a link to the user
|
||||
Hidden,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Debug, PartialOrd, Ord)]
|
||||
pub struct RustLink {
|
||||
pub path: Path,
|
||||
pub typ: DocType,
|
||||
pub display: RustLinkDisplay,
|
||||
}
|
||||
|
||||
impl Parse for RustLink {
|
||||
fn parse(input: ParseStream<'_>) -> parse::Result<Self> {
|
||||
let path = input.parse()?;
|
||||
let path = Path::from_syn(&path);
|
||||
let _comma: Token![,] = input.parse()?;
|
||||
let ty_ident: Ident = input.parse()?;
|
||||
let typ = match &*ty_ident.to_string() {
|
||||
"Struct" => DocType::Struct,
|
||||
"StructField" => DocType::StructField,
|
||||
"Enum" => DocType::Enum,
|
||||
"EnumVariant" => DocType::EnumVariant,
|
||||
"EnumVariantField" => DocType::EnumVariantField,
|
||||
"Trait" => DocType::Trait,
|
||||
"FnInStruct" => DocType::FnInStruct,
|
||||
"FnInEnum" => DocType::FnInEnum,
|
||||
"FnInTrait" => DocType::FnInTrait,
|
||||
"DefaultFnInTrait" => DocType::DefaultFnInTrait,
|
||||
"Fn" => DocType::Fn,
|
||||
"Mod" => DocType::Mod,
|
||||
"Constant" => DocType::Constant,
|
||||
"AssociatedConstantInEnum" => DocType::AssociatedConstantInEnum,
|
||||
"AssociatedConstantInTrait" => DocType::AssociatedConstantInTrait,
|
||||
"AssociatedConstantInStruct" => DocType::AssociatedConstantInStruct,
|
||||
"Macro" => DocType::Macro,
|
||||
"AssociatedTypeInEnum" => DocType::AssociatedTypeInEnum,
|
||||
"AssociatedTypeInTrait" => DocType::AssociatedTypeInTrait,
|
||||
"AssociatedTypeInStruct" => DocType::AssociatedTypeInStruct,
|
||||
"Typedef" => DocType::Typedef,
|
||||
_ => {
|
||||
return Err(parse::Error::new(
|
||||
ty_ident.span(),
|
||||
"Unknown rust_link doc type",
|
||||
))
|
||||
}
|
||||
};
|
||||
let lookahead = input.lookahead1();
|
||||
let display = if lookahead.peek(Token![,]) {
|
||||
let _comma: Token![,] = input.parse()?;
|
||||
let display_ident: Ident = input.parse()?;
|
||||
match &*display_ident.to_string() {
|
||||
"normal" => RustLinkDisplay::Normal,
|
||||
"compact" => RustLinkDisplay::Compact,
|
||||
"hidden" => RustLinkDisplay::Hidden,
|
||||
_ => return Err(parse::Error::new(display_ident.span(), "Unknown rust_link display style: Must be must be `normal`, `compact`, or `hidden`.")),
|
||||
}
|
||||
} else {
|
||||
RustLinkDisplay::Normal
|
||||
};
|
||||
Ok(RustLink { path, typ, display })
|
||||
}
|
||||
}
|
||||
impl fmt::Display for RustLink {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}#{:?}", self.path, self.typ)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Debug, PartialOrd, Ord)]
|
||||
pub enum DocType {
|
||||
Struct,
|
||||
StructField,
|
||||
Enum,
|
||||
EnumVariant,
|
||||
EnumVariantField,
|
||||
Trait,
|
||||
FnInStruct,
|
||||
FnInEnum,
|
||||
FnInTrait,
|
||||
DefaultFnInTrait,
|
||||
Fn,
|
||||
Mod,
|
||||
Constant,
|
||||
AssociatedConstantInEnum,
|
||||
AssociatedConstantInTrait,
|
||||
AssociatedConstantInStruct,
|
||||
Macro,
|
||||
AssociatedTypeInEnum,
|
||||
AssociatedTypeInTrait,
|
||||
AssociatedTypeInStruct,
|
||||
Typedef,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct DocsUrlGenerator {
|
||||
default_url: Option<String>,
|
||||
base_urls: HashMap<String, String>,
|
||||
}
|
||||
|
||||
impl DocsUrlGenerator {
|
||||
pub fn with_base_urls(default_url: Option<String>, base_urls: HashMap<String, String>) -> Self {
|
||||
Self {
|
||||
default_url,
|
||||
base_urls,
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_for_rust_link(&self, rust_link: &RustLink) -> String {
|
||||
use DocType::*;
|
||||
|
||||
let mut r = String::new();
|
||||
|
||||
let base = self
|
||||
.base_urls
|
||||
.get(rust_link.path.elements[0].as_str())
|
||||
.map(String::as_str)
|
||||
.or(self.default_url.as_deref())
|
||||
.unwrap_or("https://docs.rs/");
|
||||
|
||||
r.push_str(base);
|
||||
if !base.ends_with('/') {
|
||||
r.push('/');
|
||||
}
|
||||
if r == "https://docs.rs/" {
|
||||
r.push_str(rust_link.path.elements[0].as_str());
|
||||
r.push_str("/latest/");
|
||||
}
|
||||
|
||||
let mut elements = rust_link.path.elements.iter().peekable();
|
||||
|
||||
let module_depth = rust_link.path.elements.len()
|
||||
- match rust_link.typ {
|
||||
Mod => 0,
|
||||
Struct | Enum | Trait | Fn | Macro | Constant | Typedef => 1,
|
||||
FnInEnum
|
||||
| FnInStruct
|
||||
| FnInTrait
|
||||
| DefaultFnInTrait
|
||||
| EnumVariant
|
||||
| StructField
|
||||
| AssociatedTypeInEnum
|
||||
| AssociatedTypeInStruct
|
||||
| AssociatedTypeInTrait
|
||||
| AssociatedConstantInEnum
|
||||
| AssociatedConstantInStruct
|
||||
| AssociatedConstantInTrait => 2,
|
||||
EnumVariantField => 3,
|
||||
};
|
||||
|
||||
for _ in 0..module_depth {
|
||||
r.push_str(elements.next().unwrap().as_str());
|
||||
r.push('/');
|
||||
}
|
||||
|
||||
if elements.peek().is_none() {
|
||||
r.push_str("index.html");
|
||||
return r;
|
||||
}
|
||||
|
||||
r.push_str(match rust_link.typ {
|
||||
Typedef => "type.",
|
||||
Struct
|
||||
| StructField
|
||||
| FnInStruct
|
||||
| AssociatedTypeInStruct
|
||||
| AssociatedConstantInStruct => "struct.",
|
||||
Enum
|
||||
| EnumVariant
|
||||
| EnumVariantField
|
||||
| FnInEnum
|
||||
| AssociatedTypeInEnum
|
||||
| AssociatedConstantInEnum => "enum.",
|
||||
Trait
|
||||
| FnInTrait
|
||||
| DefaultFnInTrait
|
||||
| AssociatedTypeInTrait
|
||||
| AssociatedConstantInTrait => "trait.",
|
||||
Fn => "fn.",
|
||||
Constant => "constant.",
|
||||
Macro => "macro.",
|
||||
Mod => unreachable!(),
|
||||
});
|
||||
|
||||
r.push_str(elements.next().unwrap().as_str());
|
||||
|
||||
r.push_str(".html");
|
||||
|
||||
match rust_link.typ {
|
||||
FnInStruct | FnInEnum | DefaultFnInTrait => {
|
||||
r.push_str("#method.");
|
||||
r.push_str(elements.next().unwrap().as_str());
|
||||
}
|
||||
AssociatedTypeInStruct | AssociatedTypeInEnum | AssociatedTypeInTrait => {
|
||||
r.push_str("#associatedtype.");
|
||||
r.push_str(elements.next().unwrap().as_str());
|
||||
}
|
||||
AssociatedConstantInStruct | AssociatedConstantInEnum | AssociatedConstantInTrait => {
|
||||
r.push_str("#associatedconstant.");
|
||||
r.push_str(elements.next().unwrap().as_str());
|
||||
}
|
||||
FnInTrait => {
|
||||
r.push_str("#tymethod.");
|
||||
r.push_str(elements.next().unwrap().as_str());
|
||||
}
|
||||
EnumVariant => {
|
||||
r.push_str("#variant.");
|
||||
r.push_str(elements.next().unwrap().as_str());
|
||||
}
|
||||
StructField => {
|
||||
r.push_str("#structfield.");
|
||||
r.push_str(elements.next().unwrap().as_str());
|
||||
}
|
||||
EnumVariantField => {
|
||||
r.push_str("#variant.");
|
||||
r.push_str(elements.next().unwrap().as_str());
|
||||
r.push_str(".field.");
|
||||
r.push_str(elements.next().unwrap().as_str());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
r
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_docs_url_generator() {
|
||||
let test_cases = [
|
||||
(
|
||||
syn::parse_quote! { #[diplomat::rust_link(std::foo::bar::batz, Struct)] },
|
||||
"https://docs.rs/std/latest/std/foo/bar/struct.batz.html",
|
||||
),
|
||||
(
|
||||
syn::parse_quote! { #[diplomat::rust_link(std::foo::bar::batz, StructField)] },
|
||||
"https://docs.rs/std/latest/std/foo/struct.bar.html#structfield.batz",
|
||||
),
|
||||
(
|
||||
syn::parse_quote! { #[diplomat::rust_link(std::foo::bar::batz, Enum)] },
|
||||
"https://docs.rs/std/latest/std/foo/bar/enum.batz.html",
|
||||
),
|
||||
(
|
||||
syn::parse_quote! { #[diplomat::rust_link(std::foo::bar::batz, EnumVariant)] },
|
||||
"https://docs.rs/std/latest/std/foo/enum.bar.html#variant.batz",
|
||||
),
|
||||
(
|
||||
syn::parse_quote! { #[diplomat::rust_link(std::foo::bar::batz, EnumVariantField)] },
|
||||
"https://docs.rs/std/latest/std/enum.foo.html#variant.bar.field.batz",
|
||||
),
|
||||
(
|
||||
syn::parse_quote! { #[diplomat::rust_link(std::foo::bar::batz, Trait)] },
|
||||
"https://docs.rs/std/latest/std/foo/bar/trait.batz.html",
|
||||
),
|
||||
(
|
||||
syn::parse_quote! { #[diplomat::rust_link(std::foo::bar::batz, FnInStruct)] },
|
||||
"https://docs.rs/std/latest/std/foo/struct.bar.html#method.batz",
|
||||
),
|
||||
(
|
||||
syn::parse_quote! { #[diplomat::rust_link(std::foo::bar::batz, FnInEnum)] },
|
||||
"https://docs.rs/std/latest/std/foo/enum.bar.html#method.batz",
|
||||
),
|
||||
(
|
||||
syn::parse_quote! { #[diplomat::rust_link(std::foo::bar::batz, FnInTrait)] },
|
||||
"https://docs.rs/std/latest/std/foo/trait.bar.html#tymethod.batz",
|
||||
),
|
||||
(
|
||||
syn::parse_quote! { #[diplomat::rust_link(std::foo::bar::batz, DefaultFnInTrait)] },
|
||||
"https://docs.rs/std/latest/std/foo/trait.bar.html#method.batz",
|
||||
),
|
||||
(
|
||||
syn::parse_quote! { #[diplomat::rust_link(std::foo::bar::batz, Fn)] },
|
||||
"https://docs.rs/std/latest/std/foo/bar/fn.batz.html",
|
||||
),
|
||||
(
|
||||
syn::parse_quote! { #[diplomat::rust_link(std::foo::bar::batz, Mod)] },
|
||||
"https://docs.rs/std/latest/std/foo/bar/batz/index.html",
|
||||
),
|
||||
(
|
||||
syn::parse_quote! { #[diplomat::rust_link(std::foo::bar::batz, Constant)] },
|
||||
"https://docs.rs/std/latest/std/foo/bar/constant.batz.html",
|
||||
),
|
||||
(
|
||||
syn::parse_quote! { #[diplomat::rust_link(std::foo::bar::batz, Macro)] },
|
||||
"https://docs.rs/std/latest/std/foo/bar/macro.batz.html",
|
||||
),
|
||||
];
|
||||
|
||||
for (attr, expected) in test_cases.clone() {
|
||||
assert_eq!(
|
||||
DocsUrlGenerator::default().gen_for_rust_link(&Docs::from_attrs(&[attr]).1[0]),
|
||||
expected
|
||||
);
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
DocsUrlGenerator::with_base_urls(
|
||||
None,
|
||||
[("std".to_string(), "http://std-docs.biz/".to_string())]
|
||||
.into_iter()
|
||||
.collect()
|
||||
)
|
||||
.gen_for_rust_link(&Docs::from_attrs(&[test_cases[0].0.clone()]).1[0]),
|
||||
"http://std-docs.biz/std/foo/bar/struct.batz.html"
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
DocsUrlGenerator::with_base_urls(Some("http://std-docs.biz/".to_string()), HashMap::new())
|
||||
.gen_for_rust_link(&Docs::from_attrs(&[test_cases[0].0.clone()]).1[0]),
|
||||
"http://std-docs.biz/std/foo/bar/struct.batz.html"
|
||||
);
|
||||
}
|
112
third_party/rust/diplomat_core/src/ast/enums.rs
vendored
Normal file
112
third_party/rust/diplomat_core/src/ast/enums.rs
vendored
Normal file
@ -0,0 +1,112 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::docs::Docs;
|
||||
use super::{attrs, Ident, Method};
|
||||
use quote::ToTokens;
|
||||
|
||||
/// A fieldless enum declaration in an FFI module.
|
||||
#[derive(Clone, Serialize, Deserialize, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct Enum {
|
||||
pub name: Ident,
|
||||
pub docs: Docs,
|
||||
/// A list of variants of the enum. (name, discriminant, docs)
|
||||
pub variants: Vec<(Ident, isize, Docs)>,
|
||||
pub methods: Vec<Method>,
|
||||
pub cfg_attrs: Vec<String>,
|
||||
}
|
||||
|
||||
impl From<&syn::ItemEnum> for Enum {
|
||||
/// Extract an [`Enum`] metadata value from an AST node.
|
||||
fn from(enm: &syn::ItemEnum) -> Enum {
|
||||
let mut last_discriminant = -1;
|
||||
if !enm.generics.params.is_empty() {
|
||||
// Generic types are not allowed.
|
||||
// Assuming all enums cannot have lifetimes? We don't even have a
|
||||
// `lifetimes` field. If we change our minds we can adjust this later
|
||||
// and update the `CustomType::lifetimes` API accordingly.
|
||||
panic!("Enums cannot have generic parameters");
|
||||
}
|
||||
|
||||
let cfg_attrs = attrs::extract_cfg_attrs(&enm.attrs).collect();
|
||||
Enum {
|
||||
name: (&enm.ident).into(),
|
||||
docs: Docs::from_attrs(&enm.attrs),
|
||||
variants: enm
|
||||
.variants
|
||||
.iter()
|
||||
.map(|v| {
|
||||
let new_discriminant = v
|
||||
.discriminant
|
||||
.as_ref()
|
||||
.map(|d| {
|
||||
// Reparsing, signed literals are represented
|
||||
// as a negation expression
|
||||
let lit: Result<syn::Lit, _> = syn::parse2(d.1.to_token_stream());
|
||||
if let Ok(syn::Lit::Int(ref lit_int)) = lit {
|
||||
lit_int.base10_parse::<isize>().unwrap()
|
||||
} else {
|
||||
panic!("Expected a discriminant to be a constant integer");
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| last_discriminant + 1);
|
||||
|
||||
last_discriminant = new_discriminant;
|
||||
|
||||
(
|
||||
(&v.ident).into(),
|
||||
new_discriminant,
|
||||
Docs::from_attrs(&v.attrs),
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
methods: vec![],
|
||||
cfg_attrs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use insta::{self, Settings};
|
||||
|
||||
use syn;
|
||||
|
||||
use super::Enum;
|
||||
|
||||
#[test]
|
||||
fn simple_enum() {
|
||||
let mut settings = Settings::new();
|
||||
settings.set_sort_maps(true);
|
||||
|
||||
settings.bind(|| {
|
||||
insta::assert_yaml_snapshot!(Enum::from(&syn::parse_quote! {
|
||||
/// Some docs.
|
||||
#[diplomat::rust_link(foo::Bar, Enum)]
|
||||
enum MyLocalEnum {
|
||||
Abc,
|
||||
/// Some more docs.
|
||||
Def
|
||||
}
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn enum_with_discr() {
|
||||
let mut settings = Settings::new();
|
||||
settings.set_sort_maps(true);
|
||||
|
||||
settings.bind(|| {
|
||||
insta::assert_yaml_snapshot!(Enum::from(&syn::parse_quote! {
|
||||
/// Some docs.
|
||||
#[diplomat::rust_link(foo::Bar, Enum)]
|
||||
enum DiscriminantedEnum {
|
||||
Abc = -1,
|
||||
Def = 0,
|
||||
Ghi = 1,
|
||||
Jkl = 2,
|
||||
}
|
||||
}));
|
||||
});
|
||||
}
|
||||
}
|
86
third_party/rust/diplomat_core/src/ast/idents.rs
vendored
Normal file
86
third_party/rust/diplomat_core/src/ast/idents.rs
vendored
Normal file
@ -0,0 +1,86 @@
|
||||
use proc_macro2::Span;
|
||||
use quote::{ToTokens, TokenStreamExt};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::borrow::{Borrow, Cow};
|
||||
use std::fmt;
|
||||
|
||||
/// An identifier, analogous to `syn::Ident` and `proc_macro2::Ident`.
|
||||
#[derive(Hash, Eq, PartialEq, Serialize, Clone, Debug, Ord, PartialOrd)]
|
||||
#[serde(transparent)]
|
||||
pub struct Ident(Cow<'static, str>);
|
||||
|
||||
impl Ident {
|
||||
/// Validate a string
|
||||
fn validate(string: &str) -> syn::Result<()> {
|
||||
syn::parse_str::<syn::Ident>(string).map(|_| {})
|
||||
}
|
||||
|
||||
/// Attempt to create a new `Ident`.
|
||||
///
|
||||
/// This function fails if the input isn't valid according to
|
||||
/// `proc_macro2::Ident`'s invariants.
|
||||
pub fn try_new(string: String) -> syn::Result<Self> {
|
||||
Self::validate(&string).map(|_| Self(Cow::from(string)))
|
||||
}
|
||||
|
||||
pub fn to_syn(&self) -> syn::Ident {
|
||||
syn::Ident::new(self.as_str(), Span::call_site())
|
||||
}
|
||||
|
||||
/// Get the `&str` representation.
|
||||
pub fn as_str(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// An [`Ident`] containing "this".
|
||||
pub const THIS: Self = Ident(Cow::Borrowed("this"));
|
||||
}
|
||||
|
||||
impl From<&'static str> for Ident {
|
||||
fn from(string: &'static str) -> Self {
|
||||
Self::validate(string).unwrap();
|
||||
Self(Cow::from(string))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for Ident {
|
||||
fn from(string: String) -> Self {
|
||||
Self::validate(&string).unwrap();
|
||||
Self(Cow::from(string))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Ident {
|
||||
/// The derived `Deserialize` allows for creating `Ident`s that do not uphold
|
||||
/// the proper invariants. This custom impl ensures that this cannot happen.
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
Ok(Ident::from(String::deserialize(deserializer)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<str> for Ident {
|
||||
fn borrow(&self) -> &str {
|
||||
self.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Ident {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.as_str().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&syn::Ident> for Ident {
|
||||
fn from(ident: &syn::Ident) -> Self {
|
||||
Self(Cow::from(ident.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for Ident {
|
||||
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
|
||||
tokens.append(self.to_syn());
|
||||
}
|
||||
}
|
573
third_party/rust/diplomat_core/src/ast/lifetimes.rs
vendored
Normal file
573
third_party/rust/diplomat_core/src/ast/lifetimes.rs
vendored
Normal file
@ -0,0 +1,573 @@
|
||||
use proc_macro2::Span;
|
||||
use quote::{quote, ToTokens};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
use super::{Docs, Ident, Param, SelfParam, TypeName};
|
||||
|
||||
/// A named lifetime, e.g. `'a`.
|
||||
///
|
||||
/// # Invariants
|
||||
///
|
||||
/// Cannot be `'static` or `'_`, use [`Lifetime`] to represent those instead.
|
||||
#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, PartialOrd, Ord)]
|
||||
#[serde(transparent)]
|
||||
pub struct NamedLifetime(Ident);
|
||||
|
||||
impl NamedLifetime {
|
||||
pub fn name(&self) -> &Ident {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for NamedLifetime {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
// Special `Deserialize` impl to ensure invariants.
|
||||
let named = Ident::deserialize(deserializer)?;
|
||||
if named.as_str() == "static" {
|
||||
panic!("cannot be static");
|
||||
}
|
||||
Ok(NamedLifetime(named))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&syn::Lifetime> for NamedLifetime {
|
||||
fn from(lt: &syn::Lifetime) -> Self {
|
||||
Lifetime::from(lt).to_named().expect("cannot be static")
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&NamedLifetime> for NamedLifetime {
|
||||
fn from(this: &NamedLifetime) -> Self {
|
||||
this.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<syn::Lifetime> for NamedLifetime {
|
||||
fn eq(&self, other: &syn::Lifetime) -> bool {
|
||||
other.ident == self.0.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for NamedLifetime {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "'{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for NamedLifetime {
|
||||
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
|
||||
use proc_macro2::{Punct, Spacing};
|
||||
Punct::new('\'', Spacing::Joint).to_tokens(tokens);
|
||||
self.0.to_tokens(tokens);
|
||||
}
|
||||
}
|
||||
|
||||
/// A lifetime dependency graph used for tracking which lifetimes outlive,
|
||||
/// and are outlived by, other lifetimes.
|
||||
///
|
||||
/// It is similar to [`syn::LifetimeDef`], except it can also track lifetime
|
||||
/// bounds defined in the `where` clause.
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct LifetimeEnv {
|
||||
pub(crate) nodes: Vec<LifetimeNode>,
|
||||
}
|
||||
|
||||
impl LifetimeEnv {
|
||||
/// Construct an empty [`LifetimeEnv`].
|
||||
///
|
||||
/// To create one outside of this module, use `LifetimeEnv::from_method_item`
|
||||
/// or `LifetimeEnv::from` on `&syn::Generics`.
|
||||
fn new() -> Self {
|
||||
Self { nodes: vec![] }
|
||||
}
|
||||
|
||||
/// Iterate through the names of the lifetimes in scope.
|
||||
pub fn names(&self) -> impl Iterator<Item = &NamedLifetime> + Clone {
|
||||
self.nodes.iter().map(|node| &node.lifetime)
|
||||
}
|
||||
|
||||
/// Returns a [`LifetimeEnv`] for a method, accounting for lifetimes and bounds
|
||||
/// defined in both the impl block and the method, as well as implicit lifetime
|
||||
/// bounds in the optional `self` param, other param, and optional return type.
|
||||
/// For example, the type `&'a Foo<'b>` implies `'b: 'a`.
|
||||
pub fn from_method_item(
|
||||
method: &syn::ImplItemFn,
|
||||
impl_generics: Option<&syn::Generics>,
|
||||
self_param: Option<&SelfParam>,
|
||||
params: &[Param],
|
||||
return_type: Option<&TypeName>,
|
||||
) -> Self {
|
||||
let mut this = LifetimeEnv::new();
|
||||
// The impl generics _must_ be loaded into the env first, since the method
|
||||
// generics might use lifetimes defined in the impl, and `extend_generics`
|
||||
// panics if `'a: 'b` where `'b` isn't declared by the time it finishes.
|
||||
if let Some(generics) = impl_generics {
|
||||
this.extend_generics(generics);
|
||||
}
|
||||
this.extend_generics(&method.sig.generics);
|
||||
|
||||
if let Some(self_param) = self_param {
|
||||
this.extend_implicit_lifetime_bounds(&self_param.to_typename(), None);
|
||||
}
|
||||
for param in params {
|
||||
this.extend_implicit_lifetime_bounds(¶m.ty, None);
|
||||
}
|
||||
if let Some(return_type) = return_type {
|
||||
this.extend_implicit_lifetime_bounds(return_type, None);
|
||||
}
|
||||
|
||||
this
|
||||
}
|
||||
|
||||
/// Returns a [`LifetimeEnv`] for a struct, accounding for lifetimes and bounds
|
||||
/// defined in the struct generics, as well as implicit lifetime bounds in
|
||||
/// the struct's fields. For example, the field `&'a Foo<'b>` implies `'b: 'a`.
|
||||
pub fn from_struct_item(strct: &syn::ItemStruct, fields: &[(Ident, TypeName, Docs)]) -> Self {
|
||||
let mut this = LifetimeEnv::new();
|
||||
this.extend_generics(&strct.generics);
|
||||
for (_, typ, _) in fields {
|
||||
this.extend_implicit_lifetime_bounds(typ, None);
|
||||
}
|
||||
this
|
||||
}
|
||||
|
||||
/// Traverse a type, adding any implicit lifetime bounds that arise from
|
||||
/// having a reference to an opaque containing a lifetime.
|
||||
/// For example, the type `&'a Foo<'b>` implies `'b: 'a`.
|
||||
fn extend_implicit_lifetime_bounds(
|
||||
&mut self,
|
||||
typ: &TypeName,
|
||||
behind_ref: Option<&NamedLifetime>,
|
||||
) {
|
||||
match typ {
|
||||
TypeName::Named(path_type) => {
|
||||
if let Some(borrow_lifetime) = behind_ref {
|
||||
let explicit_longer_than_borrow =
|
||||
LifetimeTransitivity::longer_than(self, borrow_lifetime);
|
||||
let mut implicit_longer_than_borrow = vec![];
|
||||
|
||||
for path_lifetime in path_type.lifetimes.iter() {
|
||||
if let Lifetime::Named(path_lifetime) = path_lifetime {
|
||||
if !explicit_longer_than_borrow.contains(&path_lifetime) {
|
||||
implicit_longer_than_borrow.push(path_lifetime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.extend_bounds(
|
||||
implicit_longer_than_borrow
|
||||
.into_iter()
|
||||
.map(|path_lifetime| (path_lifetime, Some(borrow_lifetime))),
|
||||
);
|
||||
}
|
||||
}
|
||||
TypeName::Reference(lifetime, _, typ) => {
|
||||
let behind_ref = if let Lifetime::Named(named) = lifetime {
|
||||
Some(named)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
self.extend_implicit_lifetime_bounds(typ, behind_ref);
|
||||
}
|
||||
TypeName::Option(typ) => self.extend_implicit_lifetime_bounds(typ, None),
|
||||
TypeName::Result(ok, err, _) => {
|
||||
self.extend_implicit_lifetime_bounds(ok, None);
|
||||
self.extend_implicit_lifetime_bounds(err, None);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
/// Add the lifetimes from generic parameters and where bounds.
|
||||
fn extend_generics(&mut self, generics: &syn::Generics) {
|
||||
let generic_bounds = generics.params.iter().map(|generic| match generic {
|
||||
syn::GenericParam::Type(_) => panic!("generic types are unsupported"),
|
||||
syn::GenericParam::Lifetime(def) => (&def.lifetime, &def.bounds),
|
||||
syn::GenericParam::Const(_) => panic!("const generics are unsupported"),
|
||||
});
|
||||
|
||||
let generic_defs = generic_bounds.clone().map(|(lifetime, _)| lifetime);
|
||||
|
||||
self.extend_lifetimes(generic_defs);
|
||||
self.extend_bounds(generic_bounds);
|
||||
|
||||
if let Some(ref where_clause) = generics.where_clause {
|
||||
self.extend_bounds(where_clause.predicates.iter().map(|pred| match pred {
|
||||
syn::WherePredicate::Type(_) => panic!("trait bounds are unsupported"),
|
||||
syn::WherePredicate::Lifetime(pred) => (&pred.lifetime, &pred.bounds),
|
||||
_ => panic!("Found unknown kind of where predicate"),
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the number of lifetimes in the graph.
|
||||
pub fn len(&self) -> usize {
|
||||
self.nodes.len()
|
||||
}
|
||||
|
||||
/// Returns `true` if the graph contains no lifetimes.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.nodes.is_empty()
|
||||
}
|
||||
|
||||
/// `<'a, 'b, 'c>`
|
||||
///
|
||||
/// Write the existing lifetimes, excluding bounds, as generic parameters.
|
||||
///
|
||||
/// To include lifetime bounds, use [`LifetimeEnv::lifetime_defs_to_tokens`].
|
||||
pub fn lifetimes_to_tokens(&self) -> proc_macro2::TokenStream {
|
||||
if self.is_empty() {
|
||||
return quote! {};
|
||||
}
|
||||
|
||||
let lifetimes = self.nodes.iter().map(|node| &node.lifetime);
|
||||
quote! { <#(#lifetimes),*> }
|
||||
}
|
||||
|
||||
/// Returns the index of a lifetime in the graph, or `None` if the lifetime
|
||||
/// isn't in the graph.
|
||||
pub(crate) fn id<L>(&self, lifetime: &L) -> Option<usize>
|
||||
where
|
||||
NamedLifetime: PartialEq<L>,
|
||||
{
|
||||
self.nodes
|
||||
.iter()
|
||||
.position(|node| &node.lifetime == lifetime)
|
||||
}
|
||||
|
||||
/// Add isolated lifetimes to the graph.
|
||||
fn extend_lifetimes<'a, L, I>(&mut self, iter: I)
|
||||
where
|
||||
NamedLifetime: PartialEq<L> + From<&'a L>,
|
||||
L: 'a,
|
||||
I: IntoIterator<Item = &'a L>,
|
||||
{
|
||||
for lifetime in iter {
|
||||
if self.id(lifetime).is_some() {
|
||||
panic!(
|
||||
"lifetime name `{}` declared twice in the same scope",
|
||||
NamedLifetime::from(lifetime)
|
||||
);
|
||||
}
|
||||
|
||||
self.nodes.push(LifetimeNode {
|
||||
lifetime: lifetime.into(),
|
||||
shorter: vec![],
|
||||
longer: vec![],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Add edges to the lifetime graph.
|
||||
///
|
||||
/// This method is decoupled from [`LifetimeEnv::extend_lifetimes`] because
|
||||
/// generics can define new lifetimes, while `where` clauses cannot.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This method panics if any of the lifetime bounds aren't already defined
|
||||
/// in the graph. This isn't allowed by rustc in the first place, so it should
|
||||
/// only ever occur when deserializing an invalid [`LifetimeEnv`].
|
||||
fn extend_bounds<'a, L, B, I>(&mut self, iter: I)
|
||||
where
|
||||
NamedLifetime: PartialEq<L> + From<&'a L>,
|
||||
L: 'a,
|
||||
B: IntoIterator<Item = &'a L>,
|
||||
I: IntoIterator<Item = (&'a L, B)>,
|
||||
{
|
||||
for (lifetime, bounds) in iter {
|
||||
let long = self.id(lifetime).expect("use of undeclared lifetime, this is a bug: try calling `LifetimeEnv::extend_lifetimes` first");
|
||||
for bound in bounds {
|
||||
let short = self
|
||||
.id(bound)
|
||||
.expect("cannot use undeclared lifetime as a bound");
|
||||
self.nodes[short].longer.push(long);
|
||||
self.nodes[long].shorter.push(short);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for LifetimeEnv {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.to_token_stream().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for LifetimeEnv {
|
||||
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
|
||||
for node in self.nodes.iter() {
|
||||
let lifetime = &node.lifetime;
|
||||
if node.shorter.is_empty() {
|
||||
tokens.extend(quote! { #lifetime, });
|
||||
} else {
|
||||
let bounds = node.shorter.iter().map(|&id| &self.nodes[id].lifetime);
|
||||
tokens.extend(quote! { #lifetime: #(#bounds)+*, });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Serialize a [`LifetimeEnv`] as a map from lifetimes to their bounds.
|
||||
impl Serialize for LifetimeEnv {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
use serde::ser::SerializeMap;
|
||||
let mut seq = serializer.serialize_map(Some(self.len()))?;
|
||||
|
||||
for node in self.nodes.iter() {
|
||||
/// Helper type for serializing bounds.
|
||||
struct Bounds<'a> {
|
||||
ids: &'a [usize],
|
||||
nodes: &'a [LifetimeNode],
|
||||
}
|
||||
|
||||
impl<'a> Serialize for Bounds<'a> {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
use serde::ser::SerializeSeq;
|
||||
let mut seq = serializer.serialize_seq(Some(self.ids.len()))?;
|
||||
for &id in self.ids {
|
||||
seq.serialize_element(&self.nodes[id].lifetime)?;
|
||||
}
|
||||
seq.end()
|
||||
}
|
||||
}
|
||||
|
||||
seq.serialize_entry(
|
||||
&node.lifetime,
|
||||
&Bounds {
|
||||
ids: &node.shorter[..],
|
||||
nodes: &self.nodes,
|
||||
},
|
||||
)?;
|
||||
}
|
||||
seq.end()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for LifetimeEnv {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
let m: BTreeMap<NamedLifetime, Vec<NamedLifetime>> =
|
||||
Deserialize::deserialize(deserializer)?;
|
||||
|
||||
let mut this = LifetimeEnv::new();
|
||||
this.extend_lifetimes(m.keys());
|
||||
this.extend_bounds(m.iter());
|
||||
Ok(this)
|
||||
}
|
||||
}
|
||||
|
||||
/// A lifetime, along with ptrs to all lifetimes that are explicitly
|
||||
/// shorter/longer than it.
|
||||
///
|
||||
/// This type is internal to [`LifetimeGraph`]- the ptrs are stored as `usize`s,
|
||||
/// meaning that they may be invalid if a `LifetimeEdges` is created in one
|
||||
/// `LifetimeGraph` and then used in another.
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub(crate) struct LifetimeNode {
|
||||
/// The name of the lifetime.
|
||||
pub(crate) lifetime: NamedLifetime,
|
||||
|
||||
/// Pointers to all lifetimes that this lives _at least_ as long as.
|
||||
///
|
||||
/// Note: This doesn't account for transitivity.
|
||||
pub(crate) shorter: Vec<usize>,
|
||||
|
||||
/// Pointers to all lifetimes that live _at least_ as long as this.
|
||||
///
|
||||
/// Note: This doesn't account for transitivity.
|
||||
pub(crate) longer: Vec<usize>,
|
||||
}
|
||||
|
||||
/// A lifetime, analogous to [`syn::Lifetime`].
|
||||
#[derive(Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub enum Lifetime {
|
||||
/// The `'static` lifetime.
|
||||
Static,
|
||||
|
||||
/// A named lifetime, like `'a`.
|
||||
Named(NamedLifetime),
|
||||
|
||||
/// An elided lifetime.
|
||||
Anonymous,
|
||||
}
|
||||
|
||||
impl Lifetime {
|
||||
/// Returns the inner `NamedLifetime` if the lifetime is the `Named` variant,
|
||||
/// otherwise `None`.
|
||||
pub fn to_named(self) -> Option<NamedLifetime> {
|
||||
if let Lifetime::Named(named) = self {
|
||||
return Some(named);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Returns a reference to the inner `NamedLifetime` if the lifetime is the
|
||||
/// `Named` variant, otherwise `None`.
|
||||
pub fn as_named(&self) -> Option<&NamedLifetime> {
|
||||
if let Lifetime::Named(named) = self {
|
||||
return Some(named);
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Lifetime {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Lifetime::Static => "'static".fmt(f),
|
||||
Lifetime::Named(ref named) => named.fmt(f),
|
||||
Lifetime::Anonymous => "'_".fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for Lifetime {
|
||||
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
|
||||
match self {
|
||||
Lifetime::Static => syn::Lifetime::new("'static", Span::call_site()).to_tokens(tokens),
|
||||
Lifetime::Named(ref s) => s.to_tokens(tokens),
|
||||
Lifetime::Anonymous => syn::Lifetime::new("'_", Span::call_site()).to_tokens(tokens),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&syn::Lifetime> for Lifetime {
|
||||
fn from(lt: &syn::Lifetime) -> Self {
|
||||
if lt.ident == "static" {
|
||||
Self::Static
|
||||
} else {
|
||||
Self::Named(NamedLifetime((<.ident).into()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Option<syn::Lifetime>> for Lifetime {
|
||||
fn from(lt: &Option<syn::Lifetime>) -> Self {
|
||||
lt.as_ref().map(Into::into).unwrap_or(Self::Anonymous)
|
||||
}
|
||||
}
|
||||
|
||||
impl Lifetime {
|
||||
/// Converts the [`Lifetime`] back into an AST node that can be spliced into a program.
|
||||
pub fn to_syn(&self) -> Option<syn::Lifetime> {
|
||||
match *self {
|
||||
Self::Static => Some(syn::Lifetime::new("'static", Span::call_site())),
|
||||
Self::Anonymous => None,
|
||||
Self::Named(ref s) => Some(syn::Lifetime::new(&s.to_string(), Span::call_site())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Collect all lifetimes that are either longer_or_shorter
|
||||
pub struct LifetimeTransitivity<'env> {
|
||||
env: &'env LifetimeEnv,
|
||||
visited: Vec<bool>,
|
||||
out: Vec<&'env NamedLifetime>,
|
||||
longer_or_shorter: LongerOrShorter,
|
||||
}
|
||||
|
||||
impl<'env> LifetimeTransitivity<'env> {
|
||||
/// Returns a new [`LifetimeTransitivity`] that finds all longer lifetimes.
|
||||
pub fn longer(env: &'env LifetimeEnv) -> Self {
|
||||
Self::new(env, LongerOrShorter::Longer)
|
||||
}
|
||||
|
||||
/// Returns a new [`LifetimeTransitivity`] that finds all shorter lifetimes.
|
||||
pub fn shorter(env: &'env LifetimeEnv) -> Self {
|
||||
Self::new(env, LongerOrShorter::Shorter)
|
||||
}
|
||||
|
||||
/// Returns all the lifetimes longer than a provided `NamedLifetime`.
|
||||
pub fn longer_than(env: &'env LifetimeEnv, named: &NamedLifetime) -> Vec<&'env NamedLifetime> {
|
||||
let mut this = Self::new(env, LongerOrShorter::Longer);
|
||||
this.visit(named);
|
||||
this.finish()
|
||||
}
|
||||
|
||||
/// Returns all the lifetimes shorter than the provided `NamedLifetime`.
|
||||
pub fn shorter_than(env: &'env LifetimeEnv, named: &NamedLifetime) -> Vec<&'env NamedLifetime> {
|
||||
let mut this = Self::new(env, LongerOrShorter::Shorter);
|
||||
this.visit(named);
|
||||
this.finish()
|
||||
}
|
||||
|
||||
/// Returns a new [`LifetimeTransitivity`].
|
||||
fn new(env: &'env LifetimeEnv, longer_or_shorter: LongerOrShorter) -> Self {
|
||||
LifetimeTransitivity {
|
||||
env,
|
||||
visited: vec![false; env.len()],
|
||||
out: vec![],
|
||||
longer_or_shorter,
|
||||
}
|
||||
}
|
||||
|
||||
/// Visits a lifetime, as well as all the nodes it's transitively longer or
|
||||
/// shorter than, depending on how the `LifetimeTransitivity` was constructed.
|
||||
pub fn visit(&mut self, named: &NamedLifetime) {
|
||||
if let Some(id) = self
|
||||
.env
|
||||
.nodes
|
||||
.iter()
|
||||
.position(|node| node.lifetime == *named)
|
||||
{
|
||||
self.dfs(id);
|
||||
}
|
||||
}
|
||||
|
||||
/// Performs depth-first search through the `LifetimeEnv` created at construction
|
||||
/// for all nodes longer or shorter than the node at the provided index,
|
||||
/// depending on how the `LifetimeTransitivity` was constructed.
|
||||
fn dfs(&mut self, index: usize) {
|
||||
// Note: all of these indexings SHOULD be valid because
|
||||
// `visited.len() == nodes.len()`, and the ids come from
|
||||
// calling `Iterator::position` on `nodes`, which never shrinks.
|
||||
// So we should be able to change these to `get_unchecked`...
|
||||
if !self.visited[index] {
|
||||
self.visited[index] = true;
|
||||
|
||||
let node = &self.env.nodes[index];
|
||||
self.out.push(&node.lifetime);
|
||||
for &edge_index in self.longer_or_shorter.edges(node).iter() {
|
||||
self.dfs(edge_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the transitively reachable lifetimes.
|
||||
pub fn finish(self) -> Vec<&'env NamedLifetime> {
|
||||
self.out
|
||||
}
|
||||
}
|
||||
|
||||
/// A helper type for [`LifetimeTransitivity`] determining whether to find the
|
||||
/// transitively longer or transitively shorter lifetimes.
|
||||
enum LongerOrShorter {
|
||||
Longer,
|
||||
Shorter,
|
||||
}
|
||||
|
||||
impl LongerOrShorter {
|
||||
/// Returns either the indices of the longer or shorter lifetimes, depending
|
||||
/// on `self`.
|
||||
fn edges<'node>(&self, node: &'node LifetimeNode) -> &'node [usize] {
|
||||
match self {
|
||||
LongerOrShorter::Longer => &node.longer[..],
|
||||
LongerOrShorter::Shorter => &node.shorter[..],
|
||||
}
|
||||
}
|
||||
}
|
592
third_party/rust/diplomat_core/src/ast/methods.rs
vendored
Normal file
592
third_party/rust/diplomat_core/src/ast/methods.rs
vendored
Normal file
@ -0,0 +1,592 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
use super::attrs;
|
||||
use super::docs::Docs;
|
||||
use super::{Ident, Lifetime, LifetimeEnv, Mutability, Path, PathType, TypeName, ValidityError};
|
||||
use crate::Env;
|
||||
|
||||
/// A method declared in the `impl` associated with an FFI struct.
|
||||
/// Includes both static and non-static methods, which can be distinguished
|
||||
/// by inspecting [`Method::self_param`].
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Debug)]
|
||||
pub struct Method {
|
||||
/// The name of the method as initially declared.
|
||||
pub name: Ident,
|
||||
|
||||
/// Lines of documentation for the method.
|
||||
pub docs: Docs,
|
||||
|
||||
/// The name of the FFI function wrapping around the method.
|
||||
pub full_path_name: Ident,
|
||||
|
||||
/// The `self` param of the method, if any.
|
||||
pub self_param: Option<SelfParam>,
|
||||
|
||||
/// All non-`self` params taken by the method.
|
||||
pub params: Vec<Param>,
|
||||
|
||||
/// The return type of the method, if any.
|
||||
pub return_type: Option<TypeName>,
|
||||
|
||||
/// The lifetimes introduced in this method and surrounding impl block.
|
||||
pub lifetime_env: LifetimeEnv,
|
||||
|
||||
/// The list of `cfg` attributes (if any).
|
||||
///
|
||||
/// These are strings instead of `syn::Attribute` or `proc_macro2::TokenStream`
|
||||
/// because those types are not `PartialEq`, `Hash`, `Serialize`, etc.
|
||||
pub cfg_attrs: Vec<String>,
|
||||
}
|
||||
|
||||
impl Method {
|
||||
/// Extracts a [`Method`] from an AST node inside an `impl`.
|
||||
pub fn from_syn(
|
||||
m: &syn::ImplItemFn,
|
||||
self_path_type: PathType,
|
||||
impl_generics: Option<&syn::Generics>,
|
||||
cfg_attrs: &[String],
|
||||
) -> Method {
|
||||
let self_ident = self_path_type.path.elements.last().unwrap();
|
||||
let method_ident = &m.sig.ident;
|
||||
let extern_ident = syn::Ident::new(
|
||||
format!("{self_ident}_{method_ident}").as_str(),
|
||||
m.sig.ident.span(),
|
||||
);
|
||||
|
||||
let all_params = m
|
||||
.sig
|
||||
.inputs
|
||||
.iter()
|
||||
.filter_map(|a| match a {
|
||||
syn::FnArg::Receiver(_) => None,
|
||||
syn::FnArg::Typed(ref t) => Some(Param::from_syn(t, self_path_type.clone())),
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let self_param = m
|
||||
.sig
|
||||
.receiver()
|
||||
.map(|rec| SelfParam::from_syn(rec, self_path_type.clone()));
|
||||
|
||||
let return_ty = match &m.sig.output {
|
||||
syn::ReturnType::Type(_, return_typ) => {
|
||||
// When we allow lifetime elision, this is where we would want to
|
||||
// support it so we can insert the expanded explicit lifetimes.
|
||||
Some(TypeName::from_syn(
|
||||
return_typ.as_ref(),
|
||||
Some(self_path_type),
|
||||
))
|
||||
}
|
||||
syn::ReturnType::Default => None,
|
||||
};
|
||||
|
||||
let lifetime_env = LifetimeEnv::from_method_item(
|
||||
m,
|
||||
impl_generics,
|
||||
self_param.as_ref(),
|
||||
&all_params[..],
|
||||
return_ty.as_ref(),
|
||||
);
|
||||
|
||||
let mut cfg_attrs = cfg_attrs.to_owned();
|
||||
cfg_attrs.extend(attrs::extract_cfg_attrs(&m.attrs));
|
||||
|
||||
Method {
|
||||
name: Ident::from(method_ident),
|
||||
docs: Docs::from_attrs(&m.attrs),
|
||||
full_path_name: Ident::from(&extern_ident),
|
||||
self_param,
|
||||
params: all_params,
|
||||
return_type: return_ty,
|
||||
lifetime_env,
|
||||
cfg_attrs,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the parameters that the output is lifetime-bound to.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Given the following method:
|
||||
/// ```ignore
|
||||
/// fn foo<'a, 'b: 'a, 'c>(&'a self, bar: Bar<'b>, baz: Baz<'c>) -> FooBar<'a> { ... }
|
||||
/// ```
|
||||
/// Then this method would return the `&'a self` and `bar: Bar<'b>` params
|
||||
/// because `'a` is in the return type, and `'b` must live at least as long
|
||||
/// as `'a`. It wouldn't include `baz: Baz<'c>` though, because the return
|
||||
/// type isn't bound by `'c` in any way.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This method may panic if `TypeName::check_result_type_validity` (called by
|
||||
/// `Method::check_validity`) doesn't pass first, since the result type may
|
||||
/// contain elided lifetimes that we depend on for this method. The validity
|
||||
/// checks ensure that the return type doesn't elide any lifetimes, ensuring
|
||||
/// that this method will produce correct results.
|
||||
pub fn borrowed_params(&self) -> BorrowedParams {
|
||||
// To determine which params the return type is bound to, we just have to
|
||||
// find the params that contain a lifetime that's also in the return type.
|
||||
if let Some(ref return_type) = self.return_type {
|
||||
// The lifetimes that must outlive the return type
|
||||
let lifetimes = return_type.longer_lifetimes(&self.lifetime_env);
|
||||
|
||||
let held_self_param = self.self_param.as_ref().filter(|self_param| {
|
||||
// Check if `self` is a reference with a lifetime in the return type.
|
||||
if let Some((Lifetime::Named(ref name), _)) = self_param.reference {
|
||||
if lifetimes.contains(&name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
self_param.path_type.lifetimes.iter().any(|lt| {
|
||||
if let Lifetime::Named(name) = lt {
|
||||
lifetimes.contains(&name)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
// Collect all the params that contain a named lifetime that's also
|
||||
// in the return type.
|
||||
let held_params = self
|
||||
.params
|
||||
.iter()
|
||||
.filter_map(|param| {
|
||||
let mut lt_kind = LifetimeKind::ReturnValue;
|
||||
param
|
||||
.ty
|
||||
.visit_lifetimes(&mut |lt, _| {
|
||||
// Thanks to `TypeName::visit_lifetimes`, we can
|
||||
// traverse the lifetimes without allocations and
|
||||
// short-circuit if we find a match.
|
||||
match lt {
|
||||
Lifetime::Named(name) if lifetimes.contains(&name) => {
|
||||
return ControlFlow::Break(());
|
||||
}
|
||||
Lifetime::Static => {
|
||||
lt_kind = LifetimeKind::Static;
|
||||
return ControlFlow::Break(());
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
ControlFlow::Continue(())
|
||||
})
|
||||
.is_break()
|
||||
.then(|| (param, lt_kind))
|
||||
})
|
||||
.collect();
|
||||
|
||||
BorrowedParams(held_self_param, held_params)
|
||||
} else {
|
||||
BorrowedParams(None, vec![])
|
||||
}
|
||||
}
|
||||
|
||||
/// Performs type-specific validity checks (see [TypeName::check_validity()])
|
||||
pub fn check_validity<'a>(
|
||||
&'a self,
|
||||
in_path: &Path,
|
||||
env: &Env,
|
||||
errors: &mut Vec<ValidityError>,
|
||||
) {
|
||||
// validity check that if the self type is nonopaque, that it is
|
||||
// behind a reference
|
||||
if let Some(ref self_param) = self.self_param {
|
||||
self_param
|
||||
.to_typename()
|
||||
.check_validity(in_path, env, errors);
|
||||
}
|
||||
for m in self.params.iter() {
|
||||
// Do we need to check the validity of the input types?
|
||||
m.ty.check_validity(in_path, env, errors);
|
||||
}
|
||||
if let Some(ref t) = self.return_type {
|
||||
t.check_return_type_validity(in_path, env, errors);
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks whether the method qualifies for special writeable handling.
|
||||
/// To qualify, a method must:
|
||||
/// - not return any value
|
||||
/// - have the last argument be an `&mut diplomat_runtime::DiplomatWriteable`
|
||||
///
|
||||
/// Typically, methods of this form will be transformed in the bindings to a
|
||||
/// method that doesn't take the writeable as an argument but instead creates
|
||||
/// one locally and just returns the final string.
|
||||
pub fn is_writeable_out(&self) -> bool {
|
||||
let return_compatible = self
|
||||
.return_type
|
||||
.as_ref()
|
||||
.map(|return_type| match return_type {
|
||||
TypeName::Unit => true,
|
||||
TypeName::Result(ok, _, _) => {
|
||||
matches!(ok.as_ref(), TypeName::Unit)
|
||||
}
|
||||
_ => false,
|
||||
})
|
||||
.unwrap_or(true);
|
||||
|
||||
return_compatible && self.params.last().map(Param::is_writeable).unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Checks if any parameters are writeable (regardless of other compatibilities for writeable output)
|
||||
pub fn has_writeable_param(&self) -> bool {
|
||||
self.params.iter().any(|p| p.is_writeable())
|
||||
}
|
||||
|
||||
/// Returns the documentation block
|
||||
pub fn docs(&self) -> &Docs {
|
||||
&self.docs
|
||||
}
|
||||
}
|
||||
|
||||
/// The `self` parameter taken by a [`Method`].
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Debug)]
|
||||
pub struct SelfParam {
|
||||
/// The lifetime and mutability of the `self` param, if it's a reference.
|
||||
pub reference: Option<(Lifetime, Mutability)>,
|
||||
|
||||
/// The type of the parameter, which will be a named reference to
|
||||
/// the associated struct,
|
||||
pub path_type: PathType,
|
||||
}
|
||||
|
||||
impl SelfParam {
|
||||
pub fn to_typename(&self) -> TypeName {
|
||||
let typ = TypeName::Named(self.path_type.clone());
|
||||
if let Some((ref lifetime, ref mutability)) = self.reference {
|
||||
return TypeName::Reference(lifetime.clone(), *mutability, Box::new(typ));
|
||||
}
|
||||
typ
|
||||
}
|
||||
|
||||
pub fn from_syn(rec: &syn::Receiver, path_type: PathType) -> Self {
|
||||
SelfParam {
|
||||
reference: rec
|
||||
.reference
|
||||
.as_ref()
|
||||
.map(|(_, lt)| (lt.into(), Mutability::from_syn(&rec.mutability))),
|
||||
path_type,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A parameter taken by a [`Method`], not including `self`.
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Debug)]
|
||||
pub struct Param {
|
||||
/// The name of the parameter in the original method declaration.
|
||||
pub name: Ident,
|
||||
|
||||
/// The type of the parameter.
|
||||
pub ty: TypeName,
|
||||
}
|
||||
|
||||
impl Param {
|
||||
/// Check if this parameter is a Writeable
|
||||
pub fn is_writeable(&self) -> bool {
|
||||
match self.ty {
|
||||
TypeName::Reference(_, Mutability::Mutable, ref w) => **w == TypeName::Writeable,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_syn(t: &syn::PatType, self_path_type: PathType) -> Self {
|
||||
let ident = match t.pat.as_ref() {
|
||||
syn::Pat::Ident(ident) => ident,
|
||||
_ => panic!("Unexpected param type"),
|
||||
};
|
||||
|
||||
Param {
|
||||
name: (&ident.ident).into(),
|
||||
ty: TypeName::from_syn(&t.ty, Some(self_path_type)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The type of lifetime.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum LifetimeKind {
|
||||
/// Param must live at least as long as the returned object.
|
||||
ReturnValue,
|
||||
/// Param must live for the duration of the program.
|
||||
Static,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
/// Parameters in a method that might be borrowed in the return type.
|
||||
pub struct BorrowedParams<'a>(
|
||||
pub Option<&'a SelfParam>,
|
||||
pub Vec<(&'a Param, LifetimeKind)>,
|
||||
);
|
||||
|
||||
impl BorrowedParams<'_> {
|
||||
/// Returns an [`Iterator`] through the names of the parameters that are borrowed
|
||||
/// for the lifetime of the return value, accepting an `Ident` that the `self`
|
||||
/// param will be called if present.
|
||||
pub fn return_names<'a>(&'a self, self_name: &'a Ident) -> impl Iterator<Item = &'a Ident> {
|
||||
self.0.iter().map(move |_| self_name).chain(
|
||||
self.1.iter().filter_map(|&(param, ltk)| {
|
||||
(ltk == LifetimeKind::ReturnValue).then(|| ¶m.name)
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns an [`Iterator`] through the names of the parameters that are borrowed for a
|
||||
/// static lifetime.
|
||||
pub fn static_names(&self) -> impl Iterator<Item = &'_ Ident> {
|
||||
self.1
|
||||
.iter()
|
||||
.filter_map(|&(param, ltk)| (ltk == LifetimeKind::Static).then(|| ¶m.name))
|
||||
}
|
||||
|
||||
/// Returns `true` if a provided param name is included in the borrowed params,
|
||||
/// otherwise `false`.
|
||||
///
|
||||
/// This method doesn't check the `self` parameter. Use
|
||||
/// [`BorrowedParams::borrows_self`] instead.
|
||||
pub fn contains(&self, param_name: &Ident) -> bool {
|
||||
self.1.iter().any(|(param, _)| ¶m.name == param_name)
|
||||
}
|
||||
|
||||
/// Returns `true` if there are no borrowed parameters, otherwise `false`.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.0.is_none() && self.1.is_empty()
|
||||
}
|
||||
|
||||
/// Returns `true` if the `self` param is borrowed, otherwise `false`.
|
||||
pub fn borrows_self(&self) -> bool {
|
||||
self.0.is_some()
|
||||
}
|
||||
|
||||
/// Returns `true` if there are any borrowed params, otherwise `false`.
|
||||
pub fn borrows_params(&self) -> bool {
|
||||
!self.1.is_empty()
|
||||
}
|
||||
|
||||
/// Returns the number of borrowed params.
|
||||
pub fn len(&self) -> usize {
|
||||
self.1.len() + usize::from(self.0.is_some())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use insta;
|
||||
|
||||
use syn;
|
||||
|
||||
use crate::ast::Ident;
|
||||
|
||||
use super::{Method, Path, PathType};
|
||||
|
||||
#[test]
|
||||
fn static_methods() {
|
||||
insta::assert_yaml_snapshot!(Method::from_syn(
|
||||
&syn::parse_quote! {
|
||||
/// Some docs.
|
||||
#[diplomat::rust_link(foo::Bar::batz, FnInStruct)]
|
||||
fn foo(x: u64, y: MyCustomStruct) {
|
||||
|
||||
}
|
||||
},
|
||||
PathType::new(Path::empty().sub_path(Ident::from("MyStructContainingMethod"))),
|
||||
None,
|
||||
&[]
|
||||
));
|
||||
|
||||
insta::assert_yaml_snapshot!(Method::from_syn(
|
||||
&syn::parse_quote! {
|
||||
/// Some docs.
|
||||
/// Some more docs.
|
||||
///
|
||||
/// Even more docs.
|
||||
#[diplomat::rust_link(foo::Bar::batz, FnInEnum)]
|
||||
fn foo(x: u64, y: MyCustomStruct) -> u64 {
|
||||
x
|
||||
}
|
||||
},
|
||||
PathType::new(Path::empty().sub_path(Ident::from("MyStructContainingMethod"))),
|
||||
None,
|
||||
&[]
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cfged_method() {
|
||||
insta::assert_yaml_snapshot!(Method::from_syn(
|
||||
&syn::parse_quote! {
|
||||
/// Some docs.
|
||||
#[diplomat::rust_link(foo::Bar::batz, FnInStruct)]
|
||||
#[cfg(any(feature = "foo", not(feature = "bar")))]
|
||||
fn foo(x: u64, y: MyCustomStruct) {
|
||||
|
||||
}
|
||||
},
|
||||
PathType::new(Path::empty().sub_path(Ident::from("MyStructContainingMethod"))),
|
||||
None,
|
||||
&[]
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nonstatic_methods() {
|
||||
insta::assert_yaml_snapshot!(Method::from_syn(
|
||||
&syn::parse_quote! {
|
||||
fn foo(&self, x: u64, y: MyCustomStruct) {
|
||||
|
||||
}
|
||||
},
|
||||
PathType::new(Path::empty().sub_path(Ident::from("MyStructContainingMethod"))),
|
||||
None,
|
||||
&[]
|
||||
));
|
||||
|
||||
insta::assert_yaml_snapshot!(Method::from_syn(
|
||||
&syn::parse_quote! {
|
||||
#[diplomat::rust_link(foo::Bar::batz, FnInStruct)]
|
||||
fn foo(&mut self, x: u64, y: MyCustomStruct) -> u64 {
|
||||
x
|
||||
}
|
||||
},
|
||||
PathType::new(Path::empty().sub_path(Ident::from("MyStructContainingMethod"))),
|
||||
None,
|
||||
&[]
|
||||
));
|
||||
}
|
||||
|
||||
macro_rules! assert_borrowed_params {
|
||||
([$($return_param:ident),*] $(, [$($static_param:ident),*])? => $($tokens:tt)* ) => {{
|
||||
let method = Method::from_syn(
|
||||
&syn::parse_quote! { $($tokens)* },
|
||||
PathType::new(Path::empty().sub_path(Ident::from("MyStructContainingMethod"))),
|
||||
None,
|
||||
&[]
|
||||
);
|
||||
|
||||
let borrowed_params = method.borrowed_params();
|
||||
// The ident parser in syn doesn't allow `self`, so we use "this" as a placeholder
|
||||
// and then change it.
|
||||
let mut actual_return: Vec<&str> = borrowed_params.return_names(&Ident::THIS).map(|ident| ident.as_str()).collect();
|
||||
if borrowed_params.0.is_some() {
|
||||
actual_return[0] = "self";
|
||||
}
|
||||
let expected_return: &[&str] = &[$(stringify!($return_param)),*];
|
||||
assert_eq!(actual_return, expected_return);
|
||||
let actual_static: Vec<&str> = borrowed_params.static_names().map(|ident| ident.as_str()).collect();
|
||||
let expected_static: &[&str] = &[$($(stringify!($static_param)),*)?];
|
||||
assert_eq!(actual_static, expected_static);
|
||||
}};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn static_params_held_by_return_type() {
|
||||
assert_borrowed_params! { [first, second] =>
|
||||
#[diplomat::rust_link(foo::Bar::batz, FnInStruct)]
|
||||
fn foo<'a, 'b>(first: &'a First, second: &'b Second, third: &Third) -> Foo<'a, 'b> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
assert_borrowed_params! { [hold] =>
|
||||
#[diplomat::rust_link(Foo, FnInStruct)]
|
||||
fn transitivity<'a, 'b: 'a, 'c: 'b, 'd: 'c, 'e: 'd, 'x>(hold: &'x One<'e>, nohold: &One<'x>) -> Box<Foo<'a>> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
assert_borrowed_params! { [hold] =>
|
||||
#[diplomat::rust_link(Foo, FnInStruct)]
|
||||
fn a_le_b_and_b_le_a<'a: 'b, 'b: 'a>(hold: &'b Bar, nohold: &'c Bar) -> Box<Foo<'a>> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
assert_borrowed_params! { [a, b, c, d] =>
|
||||
#[diplomat::rust_link(Foo, FnInStruct)]
|
||||
fn many_dependents<'a, 'b: 'a, 'c: 'a, 'd: 'b, 'x, 'y>(a: &'x One<'a>, b: &'b One<'a>, c: &Two<'x, 'c>, d: &'x Two<'d, 'y>, nohold: &'x Two<'x, 'y>) -> Box<Foo<'a>> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
assert_borrowed_params! { [hold] =>
|
||||
#[diplomat::rust_link(Foo, FnInStruct)]
|
||||
fn return_outlives_param<'short, 'long: 'short>(hold: &Two<'long, 'short>, nohold: &'short One<'short>) -> Box<Foo<'long>> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
assert_borrowed_params! { [hold] =>
|
||||
#[diplomat::rust_link(Foo, FnInStruct)]
|
||||
fn transitivity_deep_types<'a, 'b: 'a, 'c: 'b, 'd: 'c>(hold: Option<Box<Bar<'d>>>, nohold: &'a Box<Option<Baz<'a>>>) -> Result<Box<Foo<'b>>, Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
assert_borrowed_params! { [top, left, right, bottom] =>
|
||||
#[diplomat::rust_link(Foo, FnInStruct)]
|
||||
fn diamond_top<'top, 'left: 'top, 'right: 'top, 'bottom: 'left + 'right>(top: One<'top>, left: One<'left>, right: One<'right>, bottom: One<'bottom>) -> Box<Foo<'top>> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
assert_borrowed_params! { [left, bottom] =>
|
||||
#[diplomat::rust_link(Foo, FnInStruct)]
|
||||
fn diamond_left<'top, 'left: 'top, 'right: 'top, 'bottom: 'left + 'right>(top: One<'top>, left: One<'left>, right: One<'right>, bottom: One<'bottom>) -> Box<Foo<'left>> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
assert_borrowed_params! { [right, bottom] =>
|
||||
#[diplomat::rust_link(Foo, FnInStruct)]
|
||||
fn diamond_right<'top, 'left: 'top, 'right: 'top, 'bottom: 'left + 'right>(top: One<'top>, left: One<'left>, right: One<'right>, bottom: One<'bottom>) -> Box<Foo<'right>> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
assert_borrowed_params! { [bottom] =>
|
||||
#[diplomat::rust_link(Foo, FnInStruct)]
|
||||
fn diamond_bottom<'top, 'left: 'top, 'right: 'top, 'bottom: 'left + 'right>(top: One<'top>, left: One<'left>, right: One<'right>, bottom: One<'bottom>) -> Box<Foo<'bottom>> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
assert_borrowed_params! { [a, b, c, d] =>
|
||||
#[diplomat::rust_link(Foo, FnInStruct)]
|
||||
fn diamond_and_nested_types<'a, 'b: 'a, 'c: 'b, 'd: 'b + 'c, 'x, 'y>(a: &'x One<'a>, b: &'y One<'b>, c: &One<'c>, d: &One<'d>, nohold: &One<'x>) -> Box<Foo<'a>> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nonstatic_params_held_by_return_type() {
|
||||
assert_borrowed_params! { [self] =>
|
||||
#[diplomat::rust_link(foo::Bar::batz, FnInStruct)]
|
||||
fn foo<'a>(&'a self) -> Foo<'a> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
assert_borrowed_params! { [self, foo, bar] =>
|
||||
#[diplomat::rust_link(foo::Bar::batz, FnInStruct)]
|
||||
fn foo<'x, 'y>(&'x self, foo: &'x Foo, bar: &Bar<'y>, baz: &Baz) -> Foo<'x, 'y> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
assert_borrowed_params! { [self, bar] =>
|
||||
#[diplomat::rust_link(foo::Bar::batz, FnInStruct)]
|
||||
fn foo<'a, 'b>(&'a self, bar: Bar<'b>) -> Foo<'a, 'b> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
assert_borrowed_params! { [self, bar], [baz] =>
|
||||
#[diplomat::rust_link(foo::Bar::batz, FnInStruct)]
|
||||
fn foo<'a, 'b>(&'a self, bar: Bar<'b>, baz: &'static str) -> Foo<'a, 'b, 'static> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
37
third_party/rust/diplomat_core/src/ast/mod.rs
vendored
Normal file
37
third_party/rust/diplomat_core/src/ast/mod.rs
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
/// As part of the macro expansion and code generation process, Diplomat
|
||||
/// generates a simplified version of the Rust AST that captures special
|
||||
/// types such as opaque structs, [`Box`], and [`Result`] with utilities
|
||||
/// for handling such types.
|
||||
pub(crate) mod attrs;
|
||||
|
||||
mod methods;
|
||||
pub use methods::{BorrowedParams, Method, Param, SelfParam};
|
||||
|
||||
mod modules;
|
||||
pub use modules::{File, Module};
|
||||
|
||||
mod structs;
|
||||
pub use structs::{OpaqueStruct, Struct};
|
||||
|
||||
mod enums;
|
||||
pub use enums::Enum;
|
||||
|
||||
mod types;
|
||||
pub use types::{
|
||||
CustomType, LifetimeOrigin, ModSymbol, Mutability, PathType, PrimitiveType, TypeName,
|
||||
};
|
||||
|
||||
mod lifetimes;
|
||||
pub use lifetimes::{Lifetime, LifetimeEnv, LifetimeTransitivity, NamedLifetime};
|
||||
|
||||
mod paths;
|
||||
pub use paths::Path;
|
||||
|
||||
mod idents;
|
||||
pub use idents::Ident;
|
||||
|
||||
mod docs;
|
||||
pub use docs::{DocType, Docs, DocsUrlGenerator, MarkdownStyle, RustLink, RustLinkDisplay};
|
||||
|
||||
mod validity;
|
||||
pub use validity::ValidityError;
|
406
third_party/rust/diplomat_core/src/ast/modules.rs
vendored
Normal file
406
third_party/rust/diplomat_core/src/ast/modules.rs
vendored
Normal file
@ -0,0 +1,406 @@
|
||||
use std::collections::{BTreeMap, HashSet};
|
||||
use std::fmt::Write as _;
|
||||
|
||||
use quote::ToTokens;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use syn::{ImplItem, Item, ItemMod, UseTree, Visibility};
|
||||
|
||||
use super::{
|
||||
attrs, CustomType, Enum, Ident, Method, ModSymbol, Mutability, OpaqueStruct, Path, PathType,
|
||||
RustLink, Struct, ValidityError,
|
||||
};
|
||||
use crate::environment::*;
|
||||
|
||||
/// Custom Diplomat attribute that can be placed on a struct definition.
|
||||
#[derive(Debug)]
|
||||
enum DiplomatStructAttribute {
|
||||
/// The `#[diplomat::out]` attribute, used for non-opaque structs that
|
||||
/// contain an owned opaque in the form of a `Box`.
|
||||
Out,
|
||||
/// The `#[diplomat::opaque]` attribute, used for marking a struct as opaque.
|
||||
/// Note that opaque structs can be borrowed in return types, but cannot
|
||||
/// be passed into a function behind a mutable reference.
|
||||
Opaque,
|
||||
/// The `#[diplomat::opaque_mut]` attribute, used for marking a struct as
|
||||
/// opaque and mutable.
|
||||
/// Note that mutable opaque structs can never be borrowed in return types
|
||||
/// (even immutably!), but can be passed into a function behind a mutable
|
||||
/// reference.
|
||||
OpaqueMut,
|
||||
}
|
||||
|
||||
impl DiplomatStructAttribute {
|
||||
/// Parses a [`DiplomatStructAttribute`] from an array of [`syn::Attribute`]s.
|
||||
/// If more than one kind is found, an error is returned containing all the
|
||||
/// ones encountered, since all the current attributes are disjoint.
|
||||
fn parse(attrs: &[syn::Attribute]) -> Result<Option<Self>, Vec<Self>> {
|
||||
let mut buf = String::with_capacity(32);
|
||||
let mut res = Ok(None);
|
||||
for attr in attrs {
|
||||
buf.clear();
|
||||
write!(&mut buf, "{}", attr.path().to_token_stream()).unwrap();
|
||||
let parsed = match buf.as_str() {
|
||||
"diplomat :: out" => Some(Self::Out),
|
||||
"diplomat :: opaque" => Some(Self::Opaque),
|
||||
"diplomat :: opaque_mut" => Some(Self::OpaqueMut),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if let Some(parsed) = parsed {
|
||||
match res {
|
||||
Ok(None) => res = Ok(Some(parsed)),
|
||||
Ok(Some(first)) => res = Err(vec![first, parsed]),
|
||||
Err(ref mut errors) => errors.push(parsed),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, Debug)]
|
||||
pub struct Module {
|
||||
pub name: Ident,
|
||||
pub imports: Vec<(Path, Ident)>,
|
||||
pub declared_types: BTreeMap<Ident, CustomType>,
|
||||
pub sub_modules: Vec<Module>,
|
||||
}
|
||||
|
||||
impl Module {
|
||||
pub fn check_validity(&self, in_path: &Path, env: &Env, errors: &mut Vec<ValidityError>) {
|
||||
self.declared_types.values().for_each(|t| {
|
||||
t.check_validity(&in_path.sub_path(self.name.clone()), env, errors);
|
||||
});
|
||||
|
||||
self.sub_modules.iter().for_each(|t| {
|
||||
t.check_validity(&in_path.sub_path(self.name.clone()), env, errors);
|
||||
});
|
||||
}
|
||||
|
||||
pub fn all_rust_links(&self) -> HashSet<&RustLink> {
|
||||
let mut rust_links = self
|
||||
.declared_types
|
||||
.values()
|
||||
.flat_map(|t| t.all_rust_links())
|
||||
.collect::<HashSet<_>>();
|
||||
|
||||
self.sub_modules.iter().for_each(|m| {
|
||||
rust_links.extend(m.all_rust_links().iter());
|
||||
});
|
||||
rust_links
|
||||
}
|
||||
|
||||
pub fn insert_all_types(&self, in_path: Path, out: &mut Env) {
|
||||
let mut mod_symbols = ModuleEnv::default();
|
||||
|
||||
self.imports.iter().for_each(|(path, name)| {
|
||||
mod_symbols.insert(name.clone(), ModSymbol::Alias(path.clone()));
|
||||
});
|
||||
|
||||
self.declared_types.iter().for_each(|(k, v)| {
|
||||
if mod_symbols
|
||||
.insert(k.clone(), ModSymbol::CustomType(v.clone()))
|
||||
.is_some()
|
||||
{
|
||||
panic!("Two types were declared with the same name, this needs to be implemented");
|
||||
}
|
||||
});
|
||||
|
||||
let path_to_self = in_path.sub_path(self.name.clone());
|
||||
self.sub_modules.iter().for_each(|m| {
|
||||
m.insert_all_types(path_to_self.clone(), out);
|
||||
mod_symbols.insert(m.name.clone(), ModSymbol::SubModule(m.name.clone()));
|
||||
});
|
||||
|
||||
out.insert(path_to_self, mod_symbols);
|
||||
}
|
||||
|
||||
pub fn from_syn(input: &ItemMod, force_analyze: bool) -> Module {
|
||||
let mut custom_types_by_name = BTreeMap::new();
|
||||
let mut sub_modules = Vec::new();
|
||||
let mut imports = Vec::new();
|
||||
|
||||
let analyze_types = force_analyze
|
||||
|| input
|
||||
.attrs
|
||||
.iter()
|
||||
.any(|a| a.path().to_token_stream().to_string() == "diplomat :: bridge");
|
||||
|
||||
input
|
||||
.content
|
||||
.as_ref()
|
||||
.map(|t| &t.1[..])
|
||||
.unwrap_or_default()
|
||||
.iter()
|
||||
.for_each(|a| match a {
|
||||
Item::Use(u) => {
|
||||
if analyze_types {
|
||||
extract_imports(&Path::empty(), &u.tree, &mut imports);
|
||||
}
|
||||
}
|
||||
Item::Struct(strct) => {
|
||||
if analyze_types {
|
||||
let custom_type = match DiplomatStructAttribute::parse(&strct.attrs[..]) {
|
||||
Ok(None) => CustomType::Struct(Struct::new(strct, false)),
|
||||
Ok(Some(DiplomatStructAttribute::Out)) => {
|
||||
CustomType::Struct(Struct::new(strct, true))
|
||||
}
|
||||
Ok(Some(DiplomatStructAttribute::Opaque)) => {
|
||||
CustomType::Opaque(OpaqueStruct::new(strct, Mutability::Immutable))
|
||||
}
|
||||
Ok(Some(DiplomatStructAttribute::OpaqueMut)) => {
|
||||
CustomType::Opaque(OpaqueStruct::new(strct, Mutability::Mutable))
|
||||
}
|
||||
Err(errors) => {
|
||||
panic!("Multiple conflicting Diplomat struct attributes, there can be at most one: {errors:?}");
|
||||
}
|
||||
};
|
||||
|
||||
custom_types_by_name.insert(Ident::from(&strct.ident), custom_type);
|
||||
}
|
||||
}
|
||||
|
||||
Item::Enum(enm) => {
|
||||
if analyze_types {
|
||||
custom_types_by_name
|
||||
.insert((&enm.ident).into(), CustomType::Enum(Enum::from(enm)));
|
||||
}
|
||||
}
|
||||
|
||||
Item::Impl(imp) => {
|
||||
if analyze_types {
|
||||
assert!(imp.trait_.is_none());
|
||||
|
||||
let self_path = match imp.self_ty.as_ref() {
|
||||
syn::Type::Path(s) => PathType::from(s),
|
||||
_ => panic!("Self type not found"),
|
||||
};
|
||||
let cfg_attrs: Vec<_> = attrs::extract_cfg_attrs(&imp.attrs).collect();
|
||||
|
||||
let mut new_methods = imp
|
||||
.items
|
||||
.iter()
|
||||
.filter_map(|i| match i {
|
||||
ImplItem::Fn(m) => Some(m),
|
||||
_ => None,
|
||||
})
|
||||
.filter(|m| matches!(m.vis, Visibility::Public(_)))
|
||||
.map(|m| Method::from_syn(m, self_path.clone(), Some(&imp.generics), &cfg_attrs))
|
||||
.collect();
|
||||
|
||||
let self_ident = self_path.path.elements.last().unwrap();
|
||||
|
||||
match custom_types_by_name.get_mut(self_ident).unwrap() {
|
||||
CustomType::Struct(strct) => {
|
||||
strct.methods.append(&mut new_methods);
|
||||
}
|
||||
CustomType::Opaque(strct) => {
|
||||
strct.methods.append(&mut new_methods);
|
||||
}
|
||||
CustomType::Enum(enm) => {
|
||||
enm.methods.append(&mut new_methods);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Item::Mod(item_mod) => {
|
||||
sub_modules.push(Module::from_syn(item_mod, false));
|
||||
}
|
||||
_ => {}
|
||||
});
|
||||
|
||||
Module {
|
||||
name: (&input.ident).into(),
|
||||
imports,
|
||||
declared_types: custom_types_by_name,
|
||||
sub_modules,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_imports(base_path: &Path, use_tree: &UseTree, out: &mut Vec<(Path, Ident)>) {
|
||||
match use_tree {
|
||||
UseTree::Name(name) => out.push((
|
||||
base_path.sub_path((&name.ident).into()),
|
||||
(&name.ident).into(),
|
||||
)),
|
||||
UseTree::Path(path) => {
|
||||
extract_imports(&base_path.sub_path((&path.ident).into()), &path.tree, out)
|
||||
}
|
||||
UseTree::Glob(_) => todo!("Glob imports are not yet supported"),
|
||||
UseTree::Group(group) => {
|
||||
group
|
||||
.items
|
||||
.iter()
|
||||
.for_each(|i| extract_imports(base_path, i, out));
|
||||
}
|
||||
UseTree::Rename(rename) => out.push((
|
||||
base_path.sub_path((&rename.ident).into()),
|
||||
(&rename.rename).into(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct File {
|
||||
pub modules: BTreeMap<String, Module>,
|
||||
}
|
||||
|
||||
impl File {
|
||||
/// Performs all necessary validity checks and returns any errors
|
||||
///
|
||||
/// Environment should be passed in from `.all_types()`
|
||||
pub fn check_validity(&self, env: &Env) -> Vec<ValidityError> {
|
||||
let mut errors = vec![];
|
||||
self.modules
|
||||
.values()
|
||||
.for_each(|t| t.check_validity(&Path::empty(), env, &mut errors));
|
||||
errors
|
||||
}
|
||||
|
||||
/// Fuses all declared types into a single environment `HashMap`.
|
||||
pub fn all_types(&self) -> Env {
|
||||
let mut out = Env::default();
|
||||
let mut top_symbols = ModuleEnv::default();
|
||||
|
||||
self.modules.values().for_each(|m| {
|
||||
m.insert_all_types(Path::empty(), &mut out);
|
||||
top_symbols.insert(m.name.clone(), ModSymbol::SubModule(m.name.clone()));
|
||||
});
|
||||
|
||||
out.insert(Path::empty(), top_symbols);
|
||||
|
||||
out
|
||||
}
|
||||
|
||||
pub fn all_rust_links(&self) -> HashSet<&RustLink> {
|
||||
self.modules
|
||||
.values()
|
||||
.flat_map(|m| m.all_rust_links().into_iter())
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&syn::File> for File {
|
||||
/// Get all custom types across all modules defined in a given file.
|
||||
fn from(file: &syn::File) -> File {
|
||||
let mut out = BTreeMap::new();
|
||||
file.items.iter().for_each(|i| {
|
||||
if let Item::Mod(item_mod) = i {
|
||||
out.insert(
|
||||
item_mod.ident.to_string(),
|
||||
Module::from_syn(item_mod, false),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
File { modules: out }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use insta::{self, Settings};
|
||||
|
||||
use syn;
|
||||
|
||||
use crate::ast::{File, Module};
|
||||
|
||||
#[test]
|
||||
fn simple_mod() {
|
||||
let mut settings = Settings::new();
|
||||
settings.set_sort_maps(true);
|
||||
|
||||
settings.bind(|| {
|
||||
insta::assert_yaml_snapshot!(Module::from_syn(
|
||||
&syn::parse_quote! {
|
||||
mod ffi {
|
||||
struct NonOpaqueStruct {
|
||||
a: i32,
|
||||
b: Box<NonOpaqueStruct>
|
||||
}
|
||||
|
||||
impl NonOpaqueStruct {
|
||||
pub fn new(x: i32) -> NonOpaqueStruct {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
pub fn set_a(&mut self, new_a: i32) {
|
||||
self.a = new_a;
|
||||
}
|
||||
}
|
||||
|
||||
#[diplomat::opaque]
|
||||
struct OpaqueStruct {
|
||||
a: SomeExternalType
|
||||
}
|
||||
|
||||
impl OpaqueStruct {
|
||||
pub fn new() -> Box<OpaqueStruct> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
pub fn get_string(&self) -> String {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
true
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn method_visibility() {
|
||||
let mut settings = Settings::new();
|
||||
settings.set_sort_maps(true);
|
||||
|
||||
settings.bind(|| {
|
||||
insta::assert_yaml_snapshot!(Module::from_syn(
|
||||
&syn::parse_quote! {
|
||||
#[diplomat::bridge]
|
||||
mod ffi {
|
||||
struct Foo {}
|
||||
|
||||
impl Foo {
|
||||
pub fn pub_fn() {
|
||||
unimplemented!()
|
||||
}
|
||||
pub(crate) fn pub_crate_fn() {
|
||||
unimplemented!()
|
||||
}
|
||||
pub(super) fn pub_super_fn() {
|
||||
unimplemented!()
|
||||
}
|
||||
fn priv_fn() {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
true
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn import_in_non_diplomat_not_analyzed() {
|
||||
let mut settings = Settings::new();
|
||||
settings.set_sort_maps(true);
|
||||
|
||||
settings.bind(|| {
|
||||
insta::assert_yaml_snapshot!(File::from(&syn::parse_quote! {
|
||||
#[diplomat::bridge]
|
||||
mod ffi {
|
||||
struct Foo {}
|
||||
}
|
||||
|
||||
mod other {
|
||||
use something::*;
|
||||
}
|
||||
}));
|
||||
});
|
||||
}
|
||||
}
|
76
third_party/rust/diplomat_core/src/ast/paths.rs
vendored
Normal file
76
third_party/rust/diplomat_core/src/ast/paths.rs
vendored
Normal file
@ -0,0 +1,76 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
use super::Ident;
|
||||
|
||||
#[derive(Hash, Eq, PartialEq, Deserialize, Serialize, Clone, Debug, Ord, PartialOrd)]
|
||||
pub struct Path {
|
||||
pub elements: Vec<Ident>,
|
||||
}
|
||||
|
||||
impl Path {
|
||||
pub fn get_super(&self) -> Path {
|
||||
let mut new_elements = self.elements.clone();
|
||||
new_elements.remove(new_elements.len() - 1);
|
||||
Path {
|
||||
elements: new_elements,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sub_path(&self, ident: Ident) -> Path {
|
||||
let mut new_elements = self.elements.clone();
|
||||
new_elements.push(ident);
|
||||
Path {
|
||||
elements: new_elements,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_syn(&self) -> syn::Path {
|
||||
syn::Path {
|
||||
leading_colon: None,
|
||||
segments: self
|
||||
.elements
|
||||
.iter()
|
||||
.map(|s| syn::PathSegment {
|
||||
ident: s.to_syn(),
|
||||
arguments: syn::PathArguments::None,
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_syn(path: &syn::Path) -> Path {
|
||||
Path {
|
||||
elements: path
|
||||
.segments
|
||||
.iter()
|
||||
.map(|seg| (&seg.ident).into())
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn empty() -> Path {
|
||||
Path { elements: vec![] }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Path {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
if let Some((head, tail)) = self.elements.split_first() {
|
||||
head.fmt(f)?;
|
||||
for seg in tail {
|
||||
"::".fmt(f)?;
|
||||
seg.fmt(f)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<Ident> for Path {
|
||||
fn from_iter<T: IntoIterator<Item = Ident>>(iter: T) -> Self {
|
||||
Path {
|
||||
elements: iter.into_iter().collect(),
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
---
|
||||
source: core/src/ast/enums.rs
|
||||
expression: "Enum::from(&syn::parse_quote! {\n /// Some docs.\n #[diplomat :: rust_link(foo :: Bar, Enum)] enum\n DiscriminantedEnum { Abc = - 1, Def = 0, Ghi = 1, Jkl = 2, }\n })"
|
||||
---
|
||||
name: DiscriminantedEnum
|
||||
docs:
|
||||
- Some docs.
|
||||
- - path:
|
||||
elements:
|
||||
- foo
|
||||
- Bar
|
||||
typ: Enum
|
||||
display: Normal
|
||||
variants:
|
||||
- - Abc
|
||||
- -1
|
||||
- - ""
|
||||
- []
|
||||
- - Def
|
||||
- 0
|
||||
- - ""
|
||||
- []
|
||||
- - Ghi
|
||||
- 1
|
||||
- - ""
|
||||
- []
|
||||
- - Jkl
|
||||
- 2
|
||||
- - ""
|
||||
- []
|
||||
methods: []
|
||||
cfg_attrs: []
|
||||
|
@ -0,0 +1,25 @@
|
||||
---
|
||||
source: core/src/ast/enums.rs
|
||||
expression: "Enum::from(&syn::parse_quote! {\n /// Some docs.\n #[diplomat :: rust_link(foo :: Bar, Enum)] enum MyLocalEnum\n {\n Abc, /// Some more docs.\n Def\n }\n })"
|
||||
---
|
||||
name: MyLocalEnum
|
||||
docs:
|
||||
- Some docs.
|
||||
- - path:
|
||||
elements:
|
||||
- foo
|
||||
- Bar
|
||||
typ: Enum
|
||||
display: Normal
|
||||
variants:
|
||||
- - Abc
|
||||
- 0
|
||||
- - ""
|
||||
- []
|
||||
- - Def
|
||||
- 1
|
||||
- - Some more docs.
|
||||
- []
|
||||
methods: []
|
||||
cfg_attrs: []
|
||||
|
@ -0,0 +1,32 @@
|
||||
---
|
||||
source: core/src/ast/methods.rs
|
||||
expression: "Method::from_syn(&syn::parse_quote! {\n /// Some docs.\n #[diplomat :: rust_link(foo :: Bar :: batz, FnInStruct)]\n #[cfg(any(feature = \"foo\", not(feature = \"bar\")))] fn\n foo(x : u64, y : MyCustomStruct) {}\n },\n PathType::new(Path::empty().sub_path(Ident::from(\"MyStructContainingMethod\"))),\n None, &[])"
|
||||
---
|
||||
name: foo
|
||||
docs:
|
||||
- Some docs.
|
||||
- - path:
|
||||
elements:
|
||||
- foo
|
||||
- Bar
|
||||
- batz
|
||||
typ: FnInStruct
|
||||
display: Normal
|
||||
full_path_name: MyStructContainingMethod_foo
|
||||
self_param: ~
|
||||
params:
|
||||
- name: x
|
||||
ty:
|
||||
Primitive: u64
|
||||
- name: y
|
||||
ty:
|
||||
Named:
|
||||
path:
|
||||
elements:
|
||||
- MyCustomStruct
|
||||
lifetimes: []
|
||||
return_type: ~
|
||||
lifetime_env: {}
|
||||
cfg_attrs:
|
||||
- "# [cfg (any (feature = \"foo\" , not (feature = \"bar\")))]"
|
||||
|
@ -0,0 +1,40 @@
|
||||
---
|
||||
source: core/src/ast/methods.rs
|
||||
expression: "Method::from_syn(&syn::parse_quote! {\n #[diplomat :: rust_link(foo :: Bar :: batz, FnInStruct)] fn\n foo(& mut self, x : u64, y : MyCustomStruct) -> u64 { x }\n },\n PathType::new(Path::empty().sub_path(Ident::from(\"MyStructContainingMethod\"))),\n None, &[])"
|
||||
---
|
||||
name: foo
|
||||
docs:
|
||||
- ""
|
||||
- - path:
|
||||
elements:
|
||||
- foo
|
||||
- Bar
|
||||
- batz
|
||||
typ: FnInStruct
|
||||
display: Normal
|
||||
full_path_name: MyStructContainingMethod_foo
|
||||
self_param:
|
||||
reference:
|
||||
- Anonymous
|
||||
- Mutable
|
||||
path_type:
|
||||
path:
|
||||
elements:
|
||||
- MyStructContainingMethod
|
||||
lifetimes: []
|
||||
params:
|
||||
- name: x
|
||||
ty:
|
||||
Primitive: u64
|
||||
- name: y
|
||||
ty:
|
||||
Named:
|
||||
path:
|
||||
elements:
|
||||
- MyCustomStruct
|
||||
lifetimes: []
|
||||
return_type:
|
||||
Primitive: u64
|
||||
lifetime_env: {}
|
||||
cfg_attrs: []
|
||||
|
@ -0,0 +1,33 @@
|
||||
---
|
||||
source: core/src/ast/methods.rs
|
||||
expression: "Method::from_syn(&syn::parse_quote! {\n fn foo(& self, x : u64, y : MyCustomStruct) {}\n },\n PathType::new(Path::empty().sub_path(Ident::from(\"MyStructContainingMethod\"))),\n None, &[])"
|
||||
---
|
||||
name: foo
|
||||
docs:
|
||||
- ""
|
||||
- []
|
||||
full_path_name: MyStructContainingMethod_foo
|
||||
self_param:
|
||||
reference:
|
||||
- Anonymous
|
||||
- Immutable
|
||||
path_type:
|
||||
path:
|
||||
elements:
|
||||
- MyStructContainingMethod
|
||||
lifetimes: []
|
||||
params:
|
||||
- name: x
|
||||
ty:
|
||||
Primitive: u64
|
||||
- name: y
|
||||
ty:
|
||||
Named:
|
||||
path:
|
||||
elements:
|
||||
- MyCustomStruct
|
||||
lifetimes: []
|
||||
return_type: ~
|
||||
lifetime_env: {}
|
||||
cfg_attrs: []
|
||||
|
@ -0,0 +1,32 @@
|
||||
---
|
||||
source: core/src/ast/methods.rs
|
||||
expression: "Method::from_syn(&syn::parse_quote! {\n /// Some docs.\n /// Some more docs.\n ///\n /// Even more docs.\n #[diplomat :: rust_link(foo :: Bar :: batz, FnInEnum)] fn\n foo(x : u64, y : MyCustomStruct) -> u64 { x }\n },\n PathType::new(Path::empty().sub_path(Ident::from(\"MyStructContainingMethod\"))),\n None, &[])"
|
||||
---
|
||||
name: foo
|
||||
docs:
|
||||
- "Some docs.\nSome more docs.\n\nEven more docs."
|
||||
- - path:
|
||||
elements:
|
||||
- foo
|
||||
- Bar
|
||||
- batz
|
||||
typ: FnInEnum
|
||||
display: Normal
|
||||
full_path_name: MyStructContainingMethod_foo
|
||||
self_param: ~
|
||||
params:
|
||||
- name: x
|
||||
ty:
|
||||
Primitive: u64
|
||||
- name: y
|
||||
ty:
|
||||
Named:
|
||||
path:
|
||||
elements:
|
||||
- MyCustomStruct
|
||||
lifetimes: []
|
||||
return_type:
|
||||
Primitive: u64
|
||||
lifetime_env: {}
|
||||
cfg_attrs: []
|
||||
|
@ -0,0 +1,31 @@
|
||||
---
|
||||
source: core/src/ast/methods.rs
|
||||
expression: "Method::from_syn(&syn::parse_quote! {\n /// Some docs.\n #[diplomat :: rust_link(foo :: Bar :: batz, FnInStruct)] fn\n foo(x : u64, y : MyCustomStruct) {}\n },\n PathType::new(Path::empty().sub_path(Ident::from(\"MyStructContainingMethod\"))),\n None, &[])"
|
||||
---
|
||||
name: foo
|
||||
docs:
|
||||
- Some docs.
|
||||
- - path:
|
||||
elements:
|
||||
- foo
|
||||
- Bar
|
||||
- batz
|
||||
typ: FnInStruct
|
||||
display: Normal
|
||||
full_path_name: MyStructContainingMethod_foo
|
||||
self_param: ~
|
||||
params:
|
||||
- name: x
|
||||
ty:
|
||||
Primitive: u64
|
||||
- name: y
|
||||
ty:
|
||||
Named:
|
||||
path:
|
||||
elements:
|
||||
- MyCustomStruct
|
||||
lifetimes: []
|
||||
return_type: ~
|
||||
lifetime_env: {}
|
||||
cfg_attrs: []
|
||||
|
@ -0,0 +1,27 @@
|
||||
---
|
||||
source: core/src/ast/modules.rs
|
||||
expression: "File::from(&syn::parse_quote! {\n #[diplomat :: bridge] mod ffi { struct Foo {} } mod other\n { use something :: * ; }\n })"
|
||||
---
|
||||
modules:
|
||||
ffi:
|
||||
name: ffi
|
||||
imports: []
|
||||
declared_types:
|
||||
Foo:
|
||||
Struct:
|
||||
name: Foo
|
||||
docs:
|
||||
- ""
|
||||
- []
|
||||
lifetimes: {}
|
||||
fields: []
|
||||
methods: []
|
||||
output_only: false
|
||||
cfg_attrs: []
|
||||
sub_modules: []
|
||||
other:
|
||||
name: other
|
||||
imports: []
|
||||
declared_types: {}
|
||||
sub_modules: []
|
||||
|
@ -0,0 +1,30 @@
|
||||
---
|
||||
source: core/src/ast/modules.rs
|
||||
expression: "Module::from_syn(&syn::parse_quote! {\n #[diplomat :: bridge] mod ffi\n {\n struct Foo {} impl Foo\n {\n pub fn pub_fn() { unimplemented! () } pub(crate) fn\n pub_crate_fn() { unimplemented! () } pub(super) fn\n pub_super_fn() { unimplemented! () } fn priv_fn()\n { unimplemented! () }\n }\n }\n }, true)"
|
||||
---
|
||||
name: ffi
|
||||
imports: []
|
||||
declared_types:
|
||||
Foo:
|
||||
Struct:
|
||||
name: Foo
|
||||
docs:
|
||||
- ""
|
||||
- []
|
||||
lifetimes: {}
|
||||
fields: []
|
||||
methods:
|
||||
- name: pub_fn
|
||||
docs:
|
||||
- ""
|
||||
- []
|
||||
full_path_name: Foo_pub_fn
|
||||
self_param: ~
|
||||
params: []
|
||||
return_type: ~
|
||||
lifetime_env: {}
|
||||
cfg_attrs: []
|
||||
output_only: false
|
||||
cfg_attrs: []
|
||||
sub_modules: []
|
||||
|
121
third_party/rust/diplomat_core/src/ast/snapshots/diplomat_core__ast__modules__tests__simple_mod.snap
vendored
Normal file
121
third_party/rust/diplomat_core/src/ast/snapshots/diplomat_core__ast__modules__tests__simple_mod.snap
vendored
Normal file
@ -0,0 +1,121 @@
|
||||
---
|
||||
source: core/src/ast/modules.rs
|
||||
expression: "Module::from_syn(&syn::parse_quote! {\n mod ffi\n {\n struct NonOpaqueStruct\n { a : i32, b : Box < NonOpaqueStruct > } impl\n NonOpaqueStruct\n {\n pub fn new(x : i32) -> NonOpaqueStruct\n { unimplemented! () ; } pub fn\n set_a(& mut self, new_a : i32) { self.a = new_a ; }\n } #[diplomat :: opaque] struct OpaqueStruct\n { a : SomeExternalType } impl OpaqueStruct\n {\n pub fn new() -> Box < OpaqueStruct > { unimplemented! () ; }\n pub fn get_string(& self) -> String { unimplemented! () }\n }\n }\n }, true)"
|
||||
---
|
||||
name: ffi
|
||||
imports: []
|
||||
declared_types:
|
||||
NonOpaqueStruct:
|
||||
Struct:
|
||||
name: NonOpaqueStruct
|
||||
docs:
|
||||
- ""
|
||||
- []
|
||||
lifetimes: {}
|
||||
fields:
|
||||
- - a
|
||||
- Primitive: i32
|
||||
- - ""
|
||||
- []
|
||||
- - b
|
||||
- Box:
|
||||
Named:
|
||||
path:
|
||||
elements:
|
||||
- NonOpaqueStruct
|
||||
lifetimes: []
|
||||
- - ""
|
||||
- []
|
||||
methods:
|
||||
- name: new
|
||||
docs:
|
||||
- ""
|
||||
- []
|
||||
full_path_name: NonOpaqueStruct_new
|
||||
self_param: ~
|
||||
params:
|
||||
- name: x
|
||||
ty:
|
||||
Primitive: i32
|
||||
return_type:
|
||||
Named:
|
||||
path:
|
||||
elements:
|
||||
- NonOpaqueStruct
|
||||
lifetimes: []
|
||||
lifetime_env: {}
|
||||
cfg_attrs: []
|
||||
- name: set_a
|
||||
docs:
|
||||
- ""
|
||||
- []
|
||||
full_path_name: NonOpaqueStruct_set_a
|
||||
self_param:
|
||||
reference:
|
||||
- Anonymous
|
||||
- Mutable
|
||||
path_type:
|
||||
path:
|
||||
elements:
|
||||
- NonOpaqueStruct
|
||||
lifetimes: []
|
||||
params:
|
||||
- name: new_a
|
||||
ty:
|
||||
Primitive: i32
|
||||
return_type: ~
|
||||
lifetime_env: {}
|
||||
cfg_attrs: []
|
||||
output_only: false
|
||||
cfg_attrs: []
|
||||
OpaqueStruct:
|
||||
Opaque:
|
||||
name: OpaqueStruct
|
||||
docs:
|
||||
- ""
|
||||
- []
|
||||
lifetimes: {}
|
||||
methods:
|
||||
- name: new
|
||||
docs:
|
||||
- ""
|
||||
- []
|
||||
full_path_name: OpaqueStruct_new
|
||||
self_param: ~
|
||||
params: []
|
||||
return_type:
|
||||
Box:
|
||||
Named:
|
||||
path:
|
||||
elements:
|
||||
- OpaqueStruct
|
||||
lifetimes: []
|
||||
lifetime_env: {}
|
||||
cfg_attrs: []
|
||||
- name: get_string
|
||||
docs:
|
||||
- ""
|
||||
- []
|
||||
full_path_name: OpaqueStruct_get_string
|
||||
self_param:
|
||||
reference:
|
||||
- Anonymous
|
||||
- Immutable
|
||||
path_type:
|
||||
path:
|
||||
elements:
|
||||
- OpaqueStruct
|
||||
lifetimes: []
|
||||
params: []
|
||||
return_type:
|
||||
Named:
|
||||
path:
|
||||
elements:
|
||||
- String
|
||||
lifetimes: []
|
||||
lifetime_env: {}
|
||||
cfg_attrs: []
|
||||
mutability: Immutable
|
||||
cfg_attrs: []
|
||||
sub_modules: []
|
||||
|
@ -0,0 +1,32 @@
|
||||
---
|
||||
source: core/src/ast/structs.rs
|
||||
expression: "Struct::new(&syn::parse_quote! {\n /// Some docs.\n #[diplomat :: rust_link(foo :: Bar, Struct)] struct\n MyLocalStruct { a : i32, b : Box < MyLocalStruct > }\n }, true)"
|
||||
---
|
||||
name: MyLocalStruct
|
||||
docs:
|
||||
- Some docs.
|
||||
- - path:
|
||||
elements:
|
||||
- foo
|
||||
- Bar
|
||||
typ: Struct
|
||||
display: Normal
|
||||
lifetimes: {}
|
||||
fields:
|
||||
- - a
|
||||
- Primitive: i32
|
||||
- - ""
|
||||
- []
|
||||
- - b
|
||||
- Box:
|
||||
Named:
|
||||
path:
|
||||
elements:
|
||||
- MyLocalStruct
|
||||
lifetimes: []
|
||||
- - ""
|
||||
- []
|
||||
methods: []
|
||||
output_only: true
|
||||
cfg_attrs: []
|
||||
|
@ -0,0 +1,12 @@
|
||||
---
|
||||
source: core/src/ast/types.rs
|
||||
expression: "TypeName::from_syn(&syn::parse_quote! { :: core :: my_type :: Foo }, None)"
|
||||
---
|
||||
Named:
|
||||
path:
|
||||
elements:
|
||||
- core
|
||||
- my_type
|
||||
- Foo
|
||||
lifetimes: []
|
||||
|
@ -0,0 +1,13 @@
|
||||
---
|
||||
source: core/src/ast/types.rs
|
||||
expression: "TypeName::from_syn(&syn::parse_quote! { :: core :: my_type :: Foo < 'test > },\n None)"
|
||||
---
|
||||
Named:
|
||||
path:
|
||||
elements:
|
||||
- core
|
||||
- my_type
|
||||
- Foo
|
||||
lifetimes:
|
||||
- Named: test
|
||||
|
@ -0,0 +1,12 @@
|
||||
---
|
||||
source: core/src/ast/types.rs
|
||||
expression: "TypeName::from_syn(&syn::parse_quote! { Option < Ref < 'object >> }, None)"
|
||||
---
|
||||
Option:
|
||||
Named:
|
||||
path:
|
||||
elements:
|
||||
- Ref
|
||||
lifetimes:
|
||||
- Named: object
|
||||
|
@ -0,0 +1,14 @@
|
||||
---
|
||||
source: core/src/ast/types.rs
|
||||
expression: "TypeName::from_syn(&syn::parse_quote! { Foo < 'a, 'b, 'c, 'd > }, None)"
|
||||
---
|
||||
Named:
|
||||
path:
|
||||
elements:
|
||||
- Foo
|
||||
lifetimes:
|
||||
- Named: a
|
||||
- Named: b
|
||||
- Named: c
|
||||
- Named: d
|
||||
|
@ -0,0 +1,18 @@
|
||||
---
|
||||
source: core/src/ast/types.rs
|
||||
expression: "TypeName::from_syn(&syn::parse_quote! {\n very :: long :: path :: to :: my :: Type < 'x, 'y, 'z >\n }, None)"
|
||||
---
|
||||
Named:
|
||||
path:
|
||||
elements:
|
||||
- very
|
||||
- long
|
||||
- path
|
||||
- to
|
||||
- my
|
||||
- Type
|
||||
lifetimes:
|
||||
- Named: x
|
||||
- Named: y
|
||||
- Named: z
|
||||
|
@ -0,0 +1,20 @@
|
||||
---
|
||||
source: core/src/ast/types.rs
|
||||
expression: "TypeName::from_syn(&syn::parse_quote! {\n Result < OkRef < 'a, 'b >, ErrRef < 'c >>\n }, None)"
|
||||
---
|
||||
Result:
|
||||
- Named:
|
||||
path:
|
||||
elements:
|
||||
- OkRef
|
||||
lifetimes:
|
||||
- Named: a
|
||||
- Named: b
|
||||
- Named:
|
||||
path:
|
||||
elements:
|
||||
- ErrRef
|
||||
lifetimes:
|
||||
- Named: c
|
||||
- true
|
||||
|
@ -0,0 +1,12 @@
|
||||
---
|
||||
source: core/src/ast/types.rs
|
||||
expression: "TypeName::from_syn(&syn::parse_quote! { Foo < 'a, 'b > }, None)"
|
||||
---
|
||||
Named:
|
||||
path:
|
||||
elements:
|
||||
- Foo
|
||||
lifetimes:
|
||||
- Named: a
|
||||
- Named: b
|
||||
|
@ -0,0 +1,11 @@
|
||||
---
|
||||
source: core/src/ast/types.rs
|
||||
expression: "TypeName::from_syn(&syn::parse_quote! { Box < MyLocalStruct > }, None)"
|
||||
---
|
||||
Box:
|
||||
Named:
|
||||
path:
|
||||
elements:
|
||||
- MyLocalStruct
|
||||
lifetimes: []
|
||||
|
@ -0,0 +1,7 @@
|
||||
---
|
||||
source: core/src/ast/types.rs
|
||||
expression: "TypeName::from_syn(&syn::parse_quote! { Box < i32 > }, None)"
|
||||
---
|
||||
Box:
|
||||
Primitive: i32
|
||||
|
@ -0,0 +1,10 @@
|
||||
---
|
||||
source: core/src/ast/types.rs
|
||||
expression: "TypeName::from_syn(&syn::parse_quote! { MyLocalStruct }, None)"
|
||||
---
|
||||
Named:
|
||||
path:
|
||||
elements:
|
||||
- MyLocalStruct
|
||||
lifetimes: []
|
||||
|
@ -0,0 +1,11 @@
|
||||
---
|
||||
source: core/src/ast/types.rs
|
||||
expression: "TypeName::from_syn(&syn::parse_quote! { Option < MyLocalStruct > }, None)"
|
||||
---
|
||||
Option:
|
||||
Named:
|
||||
path:
|
||||
elements:
|
||||
- MyLocalStruct
|
||||
lifetimes: []
|
||||
|
@ -0,0 +1,7 @@
|
||||
---
|
||||
source: core/src/ast/types.rs
|
||||
expression: "TypeName::from_syn(&syn::parse_quote! { Option < i32 > }, None)"
|
||||
---
|
||||
Option:
|
||||
Primitive: i32
|
||||
|
@ -0,0 +1,6 @@
|
||||
---
|
||||
source: core/src/ast/types.rs
|
||||
expression: "TypeName::from_syn(&syn::parse_quote! { usize }, None)"
|
||||
---
|
||||
Primitive: usize
|
||||
|
@ -0,0 +1,6 @@
|
||||
---
|
||||
source: core/src/ast/types.rs
|
||||
expression: "TypeName::from_syn(&syn::parse_quote! { bool }, None)"
|
||||
---
|
||||
Primitive: bool
|
||||
|
@ -0,0 +1,6 @@
|
||||
---
|
||||
source: core/src/ast/types.rs
|
||||
expression: "TypeName::from_syn(&syn::parse_quote! { i32 }, None)"
|
||||
---
|
||||
Primitive: i32
|
||||
|
@ -0,0 +1,13 @@
|
||||
---
|
||||
source: core/src/ast/types.rs
|
||||
expression: "TypeName::from_syn(&syn::parse_quote! { & mut MyLocalStruct }, None)"
|
||||
---
|
||||
Reference:
|
||||
- Anonymous
|
||||
- Mutable
|
||||
- Named:
|
||||
path:
|
||||
elements:
|
||||
- MyLocalStruct
|
||||
lifetimes: []
|
||||
|
@ -0,0 +1,9 @@
|
||||
---
|
||||
source: core/src/ast/types.rs
|
||||
expression: "TypeName::from_syn(&syn::parse_quote! { & i32 }, None)"
|
||||
---
|
||||
Reference:
|
||||
- Anonymous
|
||||
- Immutable
|
||||
- Primitive: i32
|
||||
|
@ -0,0 +1,13 @@
|
||||
---
|
||||
source: core/src/ast/types.rs
|
||||
expression: "TypeName::from_syn(&syn::parse_quote! {\n DiplomatResult < (), MyLocalStruct >\n }, None)"
|
||||
---
|
||||
Result:
|
||||
- Unit
|
||||
- Named:
|
||||
path:
|
||||
elements:
|
||||
- MyLocalStruct
|
||||
lifetimes: []
|
||||
- false
|
||||
|
@ -0,0 +1,13 @@
|
||||
---
|
||||
source: core/src/ast/types.rs
|
||||
expression: "TypeName::from_syn(&syn::parse_quote! { Result < MyLocalStruct, i32 > }, None)"
|
||||
---
|
||||
Result:
|
||||
- Named:
|
||||
path:
|
||||
elements:
|
||||
- MyLocalStruct
|
||||
lifetimes: []
|
||||
- Primitive: i32
|
||||
- true
|
||||
|
@ -0,0 +1,13 @@
|
||||
---
|
||||
source: core/src/ast/types.rs
|
||||
expression: "TypeName::from_syn(&syn::parse_quote! { Result < (), MyLocalStruct > }, None)"
|
||||
---
|
||||
Result:
|
||||
- Unit
|
||||
- Named:
|
||||
path:
|
||||
elements:
|
||||
- MyLocalStruct
|
||||
lifetimes: []
|
||||
- true
|
||||
|
@ -0,0 +1,13 @@
|
||||
---
|
||||
source: core/src/ast/types.rs
|
||||
expression: "TypeName::from_syn(&syn::parse_quote! {\n DiplomatResult < MyLocalStruct, i32 >\n }, None)"
|
||||
---
|
||||
Result:
|
||||
- Named:
|
||||
path:
|
||||
elements:
|
||||
- MyLocalStruct
|
||||
lifetimes: []
|
||||
- Primitive: i32
|
||||
- false
|
||||
|
@ -0,0 +1,9 @@
|
||||
---
|
||||
source: core/src/ast/validity.rs
|
||||
expression: output
|
||||
---
|
||||
A non-opaque type was found behind a Box or reference, these can only be handled by-move as they get converted at the FFI boundary: NonOpaque
|
||||
A non-opaque type was found behind a Box or reference, these can only be handled by-move as they get converted at the FFI boundary: NonOpaque
|
||||
A non-opaque type was found behind a Box or reference, these can only be handled by-move as they get converted at the FFI boundary: NonOpaque
|
||||
A non-opaque type was found behind a Box or reference, these can only be handled by-move as they get converted at the FFI boundary: NonOpaque
|
||||
|
@ -0,0 +1,7 @@
|
||||
---
|
||||
source: core/src/ast/validity.rs
|
||||
expression: output
|
||||
---
|
||||
An opaque type crossed the FFI boundary as a value: OpaqueStruct
|
||||
An opaque type crossed the FFI boundary as a value: OpaqueStruct
|
||||
|
@ -0,0 +1,6 @@
|
||||
---
|
||||
source: core/src/ast/validity.rs
|
||||
expression: output
|
||||
---
|
||||
A non-opaque zero-sized struct or enum has been defined: ffi::NonOpaqueStruct
|
||||
|
@ -0,0 +1,7 @@
|
||||
---
|
||||
source: core/src/ast/validity.rs
|
||||
expression: output
|
||||
---
|
||||
An opaque type crossed the FFI boundary as a value: MyOpaqueStruct
|
||||
An opaque type crossed the FFI boundary as a value: MyOpaqueStruct
|
||||
|
@ -0,0 +1,9 @@
|
||||
---
|
||||
source: core/src/ast/validity.rs
|
||||
expression: output
|
||||
---
|
||||
A non-reference type was found inside an Option<T>: Option<u8>
|
||||
A non-reference type was found inside an Option<T>: Option<Option<u16>>
|
||||
A non-reference type was found inside an Option<T>: Option<char>
|
||||
A non-reference type was found inside an Option<T>: Option<u16>
|
||||
|
@ -0,0 +1,5 @@
|
||||
---
|
||||
source: core/src/ast/validity.rs
|
||||
expression: output
|
||||
---
|
||||
|
@ -0,0 +1,7 @@
|
||||
---
|
||||
source: core/src/ast/validity.rs
|
||||
expression: output
|
||||
---
|
||||
A non-opaque zero-sized struct or enum has been defined: ffi::OpaqueEnum
|
||||
A non-opaque zero-sized struct or enum has been defined: ffi::OpaqueStruct
|
||||
|
109
third_party/rust/diplomat_core/src/ast/structs.rs
vendored
Normal file
109
third_party/rust/diplomat_core/src/ast/structs.rs
vendored
Normal file
@ -0,0 +1,109 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::docs::Docs;
|
||||
use super::{attrs, Ident, LifetimeEnv, Method, Mutability, PathType, TypeName};
|
||||
|
||||
/// A struct declaration in an FFI module that is not opaque.
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Debug)]
|
||||
pub struct Struct {
|
||||
pub name: Ident,
|
||||
pub docs: Docs,
|
||||
pub lifetimes: LifetimeEnv,
|
||||
pub fields: Vec<(Ident, TypeName, Docs)>,
|
||||
pub methods: Vec<Method>,
|
||||
pub output_only: bool,
|
||||
pub cfg_attrs: Vec<String>,
|
||||
}
|
||||
|
||||
impl Struct {
|
||||
/// Extract a [`Struct`] metadata value from an AST node.
|
||||
pub fn new(strct: &syn::ItemStruct, output_only: bool) -> Self {
|
||||
let self_path_type = PathType::extract_self_type(strct);
|
||||
let fields: Vec<_> = strct
|
||||
.fields
|
||||
.iter()
|
||||
.map(|field| {
|
||||
// Non-opaque tuple structs will never be allowed
|
||||
let name = field
|
||||
.ident
|
||||
.as_ref()
|
||||
.map(Into::into)
|
||||
.expect("non-opaque tuples structs are disallowed");
|
||||
let type_name = TypeName::from_syn(&field.ty, Some(self_path_type.clone()));
|
||||
let docs = Docs::from_attrs(&field.attrs);
|
||||
|
||||
(name, type_name, docs)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let lifetimes = LifetimeEnv::from_struct_item(strct, &fields[..]);
|
||||
let cfg_attrs = attrs::extract_cfg_attrs(&strct.attrs).collect();
|
||||
|
||||
Struct {
|
||||
name: (&strct.ident).into(),
|
||||
docs: Docs::from_attrs(&strct.attrs),
|
||||
lifetimes,
|
||||
fields,
|
||||
methods: vec![],
|
||||
output_only,
|
||||
cfg_attrs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A struct annotated with [`diplomat::opaque`] whose fields are not visible.
|
||||
/// Opaque structs cannot be passed by-value across the FFI boundary, so they
|
||||
/// must be boxed or passed as references.
|
||||
#[derive(Clone, Serialize, Deserialize, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct OpaqueStruct {
|
||||
pub name: Ident,
|
||||
pub docs: Docs,
|
||||
pub lifetimes: LifetimeEnv,
|
||||
pub methods: Vec<Method>,
|
||||
pub mutability: Mutability,
|
||||
pub cfg_attrs: Vec<String>,
|
||||
}
|
||||
|
||||
impl OpaqueStruct {
|
||||
/// Extract a [`OpaqueStruct`] metadata value from an AST node.
|
||||
pub fn new(strct: &syn::ItemStruct, mutability: Mutability) -> Self {
|
||||
let cfg_attrs = attrs::extract_cfg_attrs(&strct.attrs).collect();
|
||||
OpaqueStruct {
|
||||
name: Ident::from(&strct.ident),
|
||||
docs: Docs::from_attrs(&strct.attrs),
|
||||
lifetimes: LifetimeEnv::from_struct_item(strct, &[]),
|
||||
methods: vec![],
|
||||
mutability,
|
||||
cfg_attrs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use insta::{self, Settings};
|
||||
|
||||
use syn;
|
||||
|
||||
use super::Struct;
|
||||
|
||||
#[test]
|
||||
fn simple_struct() {
|
||||
let mut settings = Settings::new();
|
||||
settings.set_sort_maps(true);
|
||||
|
||||
settings.bind(|| {
|
||||
insta::assert_yaml_snapshot!(Struct::new(
|
||||
&syn::parse_quote! {
|
||||
/// Some docs.
|
||||
#[diplomat::rust_link(foo::Bar, Struct)]
|
||||
struct MyLocalStruct {
|
||||
a: i32,
|
||||
b: Box<MyLocalStruct>
|
||||
}
|
||||
},
|
||||
true
|
||||
));
|
||||
});
|
||||
}
|
||||
}
|
1219
third_party/rust/diplomat_core/src/ast/types.rs
vendored
Normal file
1219
third_party/rust/diplomat_core/src/ast/types.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
224
third_party/rust/diplomat_core/src/ast/validity.rs
vendored
Normal file
224
third_party/rust/diplomat_core/src/ast/validity.rs
vendored
Normal file
@ -0,0 +1,224 @@
|
||||
use super::{Ident, Path, TypeName};
|
||||
|
||||
#[cfg_attr(feature = "displaydoc", derive(displaydoc::Display))]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ValidityError {
|
||||
/// An oqaue type crosses the FFI boundary as a value.
|
||||
#[cfg_attr(
|
||||
feature = "displaydoc",
|
||||
displaydoc("An opaque type crossed the FFI boundary as a value: {0}")
|
||||
)]
|
||||
OpaqueAsValue(TypeName),
|
||||
/// A non-oquare zero-sized struct or enum has been defined.
|
||||
#[cfg_attr(
|
||||
feature = "displaydoc",
|
||||
displaydoc("A non-opaque zero-sized struct or enum has been defined: {0}")
|
||||
)]
|
||||
NonOpaqueZST(Path),
|
||||
/// A non-opaque type was found behind a `Box` or reference.
|
||||
#[cfg_attr(
|
||||
feature = "displaydoc",
|
||||
displaydoc(
|
||||
"A non-opaque type was found behind a Box or reference, these can \
|
||||
only be handled by-move as they get converted at the FFI boundary: {0}"
|
||||
)
|
||||
)]
|
||||
NonOpaqueBehindRef(TypeName),
|
||||
/// A non-reference type was found inside an `Option<T>`.
|
||||
#[cfg_attr(
|
||||
feature = "displaydoc",
|
||||
displaydoc("A non-reference type was found inside an Option<T>: {0}")
|
||||
)]
|
||||
OptionNotContainingPointer(TypeName),
|
||||
/// A return type contains elided lifetimes.
|
||||
#[cfg_attr(
|
||||
feature = "displaydoc",
|
||||
displaydoc("A return type contains elided lifetimes, which aren't yet supported: {sub_type} in {full_type}")
|
||||
)]
|
||||
LifetimeElisionInReturn {
|
||||
full_type: TypeName,
|
||||
sub_type: TypeName,
|
||||
},
|
||||
/// An alias or submodule was found instead of a custom type.
|
||||
#[cfg_attr(
|
||||
feature = "displaydoc",
|
||||
displaydoc("An alias or submodule was found instead of a custom type with the name {0}.")
|
||||
)]
|
||||
PathTypeNameConflict(Ident),
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::fmt::Write;
|
||||
|
||||
macro_rules! uitest_validity {
|
||||
($($file:tt)*) => {
|
||||
let parsed: syn::File = syn::parse_quote! { $($file)* };
|
||||
let custom_types = crate::ast::File::from(&parsed);
|
||||
let env = custom_types.all_types();
|
||||
|
||||
let errors = custom_types.check_validity(&env);
|
||||
|
||||
let mut output = String::new();
|
||||
for error in errors {
|
||||
write!(&mut output, "{}\n", error).unwrap();
|
||||
}
|
||||
insta::with_settings!({}, {
|
||||
insta::assert_display_snapshot!(output)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_opaque_ffi() {
|
||||
uitest_validity! {
|
||||
#[diplomat::bridge]
|
||||
mod ffi {
|
||||
#[diplomat::opaque]
|
||||
struct MyOpaqueStruct(UnknownType);
|
||||
|
||||
impl MyOpaqueStruct {
|
||||
pub fn new() -> Box<MyOpaqueStruct> {}
|
||||
pub fn new_broken() -> MyOpaqueStruct {}
|
||||
pub fn do_thing(&self) {}
|
||||
pub fn do_thing_broken(self) {}
|
||||
pub fn broken_differently(&self, x: &MyOpaqueStruct) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn opaque_checks_with_safe_use() {
|
||||
uitest_validity! {
|
||||
#[diplomat::bridge]
|
||||
mod ffi {
|
||||
struct NonOpaqueStruct {}
|
||||
|
||||
impl NonOpaqueStruct {
|
||||
fn new(x: i32) -> NonOpaqueStruct {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
#[diplomat::opaque]
|
||||
struct OpaqueStruct {}
|
||||
|
||||
impl OpaqueStruct {
|
||||
pub fn new() -> Box<OpaqueStruct> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
pub fn get_i32(&self) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn opaque_checks_with_error() {
|
||||
uitest_validity! {
|
||||
#[diplomat::bridge]
|
||||
mod ffi {
|
||||
#[diplomat::opaque]
|
||||
struct OpaqueStruct {}
|
||||
|
||||
impl OpaqueStruct {
|
||||
pub fn new() -> OpaqueStruct {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
pub fn get_i32(self) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn zst_non_opaque() {
|
||||
uitest_validity! {
|
||||
#[diplomat::bridge]
|
||||
mod ffi {
|
||||
struct OpaqueStruct;
|
||||
|
||||
enum OpaqueEnum {}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn option_invalid() {
|
||||
uitest_validity! {
|
||||
#[diplomat::bridge]
|
||||
mod ffi {
|
||||
use diplomat_runtime::DiplomatResult;
|
||||
struct Foo {
|
||||
field: Option<u8>,
|
||||
}
|
||||
|
||||
impl Foo {
|
||||
pub fn do_thing(opt: Option<Option<u16>>) {
|
||||
|
||||
}
|
||||
|
||||
pub fn do_thing2(opt: DiplomatResult<Option<char>, u8>) {
|
||||
|
||||
}
|
||||
pub fn do_thing2(opt: Option<u16>) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn option_valid() {
|
||||
uitest_validity! {
|
||||
#[diplomat::bridge]
|
||||
mod ffi {
|
||||
struct Foo {
|
||||
field: Option<Box<u8>>,
|
||||
}
|
||||
|
||||
impl Foo {
|
||||
pub fn do_thing(opt: Option<Box<u32>>) {
|
||||
|
||||
}
|
||||
pub fn do_thing2(opt: Option<&u32>) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn non_opaque_move() {
|
||||
uitest_validity! {
|
||||
#[diplomat::bridge]
|
||||
mod ffi {
|
||||
struct NonOpaque {
|
||||
num: u8,
|
||||
}
|
||||
|
||||
impl NonOpaque {
|
||||
pub fn foo(&self) {}
|
||||
}
|
||||
|
||||
#[diplomat::opaque]
|
||||
struct Opaque;
|
||||
|
||||
impl Opaque {
|
||||
pub fn bar<'a>(&'a self) -> &'a NonOpaque {}
|
||||
pub fn baz<'a>(&'a self, x: &'a NonOpaque) {}
|
||||
pub fn quux(&self) -> Box<NonOpaque> {}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
88
third_party/rust/diplomat_core/src/environment.rs
vendored
Normal file
88
third_party/rust/diplomat_core/src/environment.rs
vendored
Normal file
@ -0,0 +1,88 @@
|
||||
use crate::ast::*;
|
||||
use std::collections::BTreeMap;
|
||||
use std::ops::Index;
|
||||
|
||||
/// The type resolution environment
|
||||
///
|
||||
/// Also contains the entire module structure
|
||||
#[derive(Default, Clone)]
|
||||
pub struct Env {
|
||||
pub(crate) env: BTreeMap<Path, ModuleEnv>,
|
||||
}
|
||||
|
||||
/// The type resolution environment within a specific module
|
||||
#[derive(Default, Clone)]
|
||||
pub struct ModuleEnv {
|
||||
pub(crate) module: BTreeMap<Ident, ModSymbol>,
|
||||
}
|
||||
|
||||
impl Env {
|
||||
pub(crate) fn insert(&mut self, path: Path, module: ModuleEnv) {
|
||||
self.env.insert(path, module);
|
||||
}
|
||||
|
||||
/// Given a path to a module and a name, get the item, if any
|
||||
pub fn get(&self, path: &Path, name: &str) -> Option<&ModSymbol> {
|
||||
self.env.get(path).and_then(|m| m.module.get(name))
|
||||
}
|
||||
|
||||
/// Iterate over all items in the environment
|
||||
///
|
||||
/// This will occur in a stable lexically sorted order by path and then name
|
||||
pub fn iter_items(&self) -> impl Iterator<Item = (&Path, &Ident, &ModSymbol)> + '_ {
|
||||
self.env
|
||||
.iter()
|
||||
.flat_map(|(k, v)| v.module.iter().map(move |v2| (k, v2.0, v2.1)))
|
||||
}
|
||||
|
||||
/// Iterate over all modules
|
||||
///
|
||||
/// This will occur in a stable lexically sorted order by path
|
||||
pub fn iter_modules(&self) -> impl Iterator<Item = (&Path, &ModuleEnv)> + '_ {
|
||||
self.env.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl ModuleEnv {
|
||||
pub(crate) fn insert(&mut self, name: Ident, symbol: ModSymbol) -> Option<ModSymbol> {
|
||||
self.module.insert(name, symbol)
|
||||
}
|
||||
|
||||
/// Given an item name, fetch it
|
||||
pub fn get(&self, name: &str) -> Option<&ModSymbol> {
|
||||
self.module.get(name)
|
||||
}
|
||||
|
||||
/// Iterate over all name-item pairs in this module
|
||||
pub fn iter(&self) -> impl Iterator<Item = (&Ident, &ModSymbol)> + '_ {
|
||||
self.module.iter()
|
||||
}
|
||||
|
||||
/// Iterate over all names in this module
|
||||
///
|
||||
/// This will occur in a stable lexically sorted order by name
|
||||
pub fn names(&self) -> impl Iterator<Item = &Ident> + '_ {
|
||||
self.module.keys()
|
||||
}
|
||||
|
||||
/// Iterate over all items in this module
|
||||
///
|
||||
/// This will occur in a stable lexically sorted order by name
|
||||
pub fn items(&self) -> impl Iterator<Item = &ModSymbol> + '_ {
|
||||
self.module.values()
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<&Path> for Env {
|
||||
type Output = ModuleEnv;
|
||||
fn index(&self, i: &Path) -> &ModuleEnv {
|
||||
&self.env[i]
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<&str> for ModuleEnv {
|
||||
type Output = ModSymbol;
|
||||
fn index(&self, i: &str) -> &ModSymbol {
|
||||
&self.module[i]
|
||||
}
|
||||
}
|
166
third_party/rust/diplomat_core/src/hir/defs.rs
vendored
Normal file
166
third_party/rust/diplomat_core/src/hir/defs.rs
vendored
Normal file
@ -0,0 +1,166 @@
|
||||
//! Type definitions for structs, output structs, opaque structs, and enums.
|
||||
|
||||
use super::{Everywhere, IdentBuf, Method, OutputOnly, TyPosition, Type};
|
||||
use crate::ast::Docs;
|
||||
|
||||
pub enum ReturnableStructDef<'tcx> {
|
||||
Struct(&'tcx StructDef),
|
||||
OutStruct(&'tcx OutStructDef),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum TypeDef<'tcx> {
|
||||
Struct(&'tcx StructDef),
|
||||
OutStruct(&'tcx OutStructDef),
|
||||
Opaque(&'tcx OpaqueDef),
|
||||
Enum(&'tcx EnumDef),
|
||||
}
|
||||
|
||||
/// Structs that can only be returned from methods.
|
||||
pub type OutStructDef = StructDef<OutputOnly>;
|
||||
|
||||
/// Structs that can be either inputs or outputs in methods.
|
||||
#[derive(Debug)]
|
||||
pub struct StructDef<P: TyPosition = Everywhere> {
|
||||
pub docs: Docs,
|
||||
pub name: IdentBuf,
|
||||
pub fields: Vec<StructField<P>>,
|
||||
pub methods: Vec<Method>,
|
||||
}
|
||||
|
||||
/// A struct whose contents are opaque across the FFI boundary, and can only
|
||||
/// cross when behind a pointer.
|
||||
///
|
||||
/// All opaques can be inputs or outputs when behind a reference, but owned
|
||||
/// opaques can only be returned since there isn't a general way for most languages
|
||||
/// to give up ownership.
|
||||
///
|
||||
/// A struct marked with `#[diplomat::opaque]`.
|
||||
#[derive(Debug)]
|
||||
pub struct OpaqueDef {
|
||||
pub docs: Docs,
|
||||
pub name: IdentBuf,
|
||||
pub methods: Vec<Method>,
|
||||
}
|
||||
|
||||
/// The enum type.
|
||||
#[derive(Debug)]
|
||||
pub struct EnumDef {
|
||||
pub docs: Docs,
|
||||
pub name: IdentBuf,
|
||||
pub variants: Vec<EnumVariant>,
|
||||
pub methods: Vec<Method>,
|
||||
}
|
||||
|
||||
/// A field on a [`OutStruct`]s.
|
||||
pub type OutStructField = StructField<OutputOnly>;
|
||||
|
||||
/// A field on a [`Struct`]s.
|
||||
#[derive(Debug)]
|
||||
pub struct StructField<P: TyPosition = Everywhere> {
|
||||
pub docs: Docs,
|
||||
pub name: IdentBuf,
|
||||
pub ty: Type<P>,
|
||||
}
|
||||
|
||||
/// A variant of an [`Enum`].
|
||||
#[derive(Debug)]
|
||||
pub struct EnumVariant {
|
||||
pub docs: Docs,
|
||||
pub name: IdentBuf,
|
||||
pub discriminant: isize,
|
||||
}
|
||||
|
||||
impl<P: TyPosition> StructDef<P> {
|
||||
pub(super) fn new(
|
||||
docs: Docs,
|
||||
name: IdentBuf,
|
||||
fields: Vec<StructField<P>>,
|
||||
methods: Vec<Method>,
|
||||
) -> Self {
|
||||
Self {
|
||||
docs,
|
||||
name,
|
||||
fields,
|
||||
methods,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl OpaqueDef {
|
||||
pub(super) fn new(docs: Docs, name: IdentBuf, methods: Vec<Method>) -> Self {
|
||||
Self {
|
||||
docs,
|
||||
name,
|
||||
methods,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EnumDef {
|
||||
pub(super) fn new(
|
||||
docs: Docs,
|
||||
name: IdentBuf,
|
||||
variants: Vec<EnumVariant>,
|
||||
methods: Vec<Method>,
|
||||
) -> Self {
|
||||
Self {
|
||||
docs,
|
||||
name,
|
||||
variants,
|
||||
methods,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a StructDef> for TypeDef<'a> {
|
||||
fn from(x: &'a StructDef) -> Self {
|
||||
TypeDef::Struct(x)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a OutStructDef> for TypeDef<'a> {
|
||||
fn from(x: &'a OutStructDef) -> Self {
|
||||
TypeDef::OutStruct(x)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a OpaqueDef> for TypeDef<'a> {
|
||||
fn from(x: &'a OpaqueDef) -> Self {
|
||||
TypeDef::Opaque(x)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a EnumDef> for TypeDef<'a> {
|
||||
fn from(x: &'a EnumDef) -> Self {
|
||||
TypeDef::Enum(x)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> TypeDef<'tcx> {
|
||||
pub fn name(&self) -> &'tcx IdentBuf {
|
||||
match *self {
|
||||
Self::Struct(ty) => &ty.name,
|
||||
Self::OutStruct(ty) => &ty.name,
|
||||
Self::Opaque(ty) => &ty.name,
|
||||
Self::Enum(ty) => &ty.name,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn docs(&self) -> &'tcx Docs {
|
||||
match *self {
|
||||
Self::Struct(ty) => &ty.docs,
|
||||
Self::OutStruct(ty) => &ty.docs,
|
||||
Self::Opaque(ty) => &ty.docs,
|
||||
Self::Enum(ty) => &ty.docs,
|
||||
}
|
||||
}
|
||||
pub fn methods(&self) -> &'tcx [Method] {
|
||||
match *self {
|
||||
Self::Struct(ty) => &ty.methods,
|
||||
Self::OutStruct(ty) => &ty.methods,
|
||||
Self::Opaque(ty) => &ty.methods,
|
||||
Self::Enum(ty) => &ty.methods,
|
||||
}
|
||||
}
|
||||
}
|
549
third_party/rust/diplomat_core/src/hir/elision.rs
vendored
Normal file
549
third_party/rust/diplomat_core/src/hir/elision.rs
vendored
Normal file
@ -0,0 +1,549 @@
|
||||
//! This module provides the functionality for lowering lifetimes from the AST
|
||||
//! to the HIR, while simultaneously inferencing elided lifetimes.
|
||||
//!
|
||||
//! Full elision rules can be found in the [Nomicon].
|
||||
//!
|
||||
//! The key factor about lifetime elision is that all elision in the output of
|
||||
//! the method (if there is any) corresponds to exactly one lifetime in the method
|
||||
//! arguments, which may or may not be elided. Therefore, our task is to find this
|
||||
//! potential lifetime first, so that if we encounter an elided lifetime while
|
||||
//! lowering the output, we know which lifetime it corresponds to.
|
||||
//!
|
||||
//! # Unspoken Rules of Elision.
|
||||
//!
|
||||
//! Broadly speaking, the Nomicon defines the elision rules are such:
|
||||
//! 1. If there's a `&self` or `&mut self`, the lifetime of that borrow
|
||||
//! corresponds to elision in the output.
|
||||
//! 2. Otherwise, if there's exactly one lifetime in the input, then that lifetime
|
||||
//! corresponds to elision in the output.
|
||||
//! 3. If neither of these cases hold, then the output cannot contain elision.
|
||||
//!
|
||||
//! What the Nomicon doesn't tell you is that there are weird corner cases around
|
||||
//! using the `Self` type. Specifically, lifetimes in the `Self` type and in the
|
||||
//! type of the `self` argument (optional) aren't considered when figuring out
|
||||
//! which lifetime should correspond to elision in the output.
|
||||
//!
|
||||
//! Check out the following code:
|
||||
//! ```compile_fail
|
||||
//! struct Foo<'a>(&'a str);
|
||||
//!
|
||||
//! impl<'a> Foo<'a> {
|
||||
//! fn get(self) -> &str { self.0 }
|
||||
//! }
|
||||
//! ```
|
||||
//! This code will fail to compile because it doesn't look at the `'a` in the
|
||||
//! `Foo<'a>`, which is what the type of `self` expands to. Therefore, it will
|
||||
//! conclude that there's nothing for the output to borrow from.
|
||||
//! This can be fixed by returning `&'a str` though. Many of the design
|
||||
//! decisions in this module were made to be able to replicate this behavior.
|
||||
//!
|
||||
//! You may be asking "why would we care about rejecting code that rustc rejects
|
||||
//! before it reaches us?" And the answer is this:
|
||||
//! ```rust
|
||||
//! # struct Foo<'a>(&'a str);
|
||||
//! impl<'a> Foo<'a> {
|
||||
//! fn get(self, s: &str) -> &str { s }
|
||||
//! }
|
||||
//! ```
|
||||
//! This code is accepted by rustc, since it only considers the lifetime of `s`
|
||||
//! when searching for a lifetime that corresponds to output elision. If we were
|
||||
//! to naively look at all the lifetimes, we would see the lifetime in the `self`
|
||||
//! argument and the lifetime of `s`, making us reject this method. Therefore, we
|
||||
//! have to be extremely careful when traversing lifetimes, and make sure that
|
||||
//! lifetimes of `Self` are lowered but _not_ considered for elision, while other
|
||||
//! lifetimes are lowered while also being considered for elision.
|
||||
//!
|
||||
//! # Lowering and Inference
|
||||
//!
|
||||
//! Lowering and elision inference is broken into three distinct stages:
|
||||
//! 1. Lowering the borrow in `&self` or `&mut self`, if there is one.
|
||||
//! 2. Lowering lifetimes of other params.
|
||||
//! 3. Lowering lifetimes of the output.
|
||||
//!
|
||||
//! Although each stage fundementally lowers lifetimes, they behave differently
|
||||
//! when lowering elided lifetimes. Naturally, this module represents each stage
|
||||
//! as a state in a state machine.
|
||||
//!
|
||||
//! The first state is represented by the [`SelfParamLifetimeLowerer`] type.
|
||||
//! Since there is either zero or one occurrences of `&self` or `&mut self`, it
|
||||
//! exposes the `.no_self_ref()` and `.lower_self_ref(lt)` methods respectively,
|
||||
//! which consume the `SelfParamLifetimeLowerer` and return the next state,
|
||||
//! [`ParamLifetimeLowerer`], as well as the lowered lifetime. The reason these
|
||||
//! are two distinct types is that the lifetime in `&self` and `&mut self` takes
|
||||
//! precedence over any other lifetimes in the input, so `.lower_self_ref(lt)`
|
||||
//! tells the next state that the candidate lifetime is already found, and to
|
||||
//! generate fresh anonymous lifetimes for any elided lifetimes.
|
||||
//!
|
||||
//! The second state is represented by the [`ParamLifetimeLowerer`] type.
|
||||
//! It implements a helper trait, [`LifetimeLowerer`], which abstracts the lowering
|
||||
//! of references and generic lifetimes. Internally, it wraps an [`ElisionSource`],
|
||||
//! which acts as a state machine for tracking candidate lifetimes to correspond
|
||||
//! to elision in the output. When a lifetime that's not in the type of the `self`
|
||||
//! argument or in the expanded generics of the `Self` type is visited, this
|
||||
//! state machine is potentially updated to another state. If the lifetime is
|
||||
//! anonymous, it's added to the internal list of nodes that go into the final
|
||||
//! [`LifetimeEnv`] after lowering. Once all the lifetimes in the input are
|
||||
//! lowered, the `into_return_ltl()` method is called to transition into the
|
||||
//! final state.
|
||||
//!
|
||||
//! The third and final state is represented by the [`ReturnLifetimeLowerer`] type.
|
||||
//! Similar to `ParamLifetimeLowerer`, it also implements the [`LifetimeLowerer`]
|
||||
//! helper trait. However, it differs from `ParamLifetimeLowerer` since instead
|
||||
//! of potentially updating the internal `ElisionSource` when visiting a lifetime,
|
||||
//! it instead reads from it when an elided lifetime occurs. Once all the output
|
||||
//! lifetimes are lowered, `.finish()` is called to return the finalized
|
||||
//! [`LifetimeEnv`].
|
||||
//!
|
||||
//! [Nomicon]: https://doc.rust-lang.org/nomicon/lifetime-elision.html
|
||||
|
||||
use super::{
|
||||
lower_ident, Lifetime, LifetimeEnv, LoweringError, MaybeStatic, MethodLifetime, TypeLifetime,
|
||||
TypeLifetimes,
|
||||
};
|
||||
use crate::ast;
|
||||
use smallvec::SmallVec;
|
||||
|
||||
/// Lower [`ast::Lifetime`]s to [`TypeLifetime`]s.
|
||||
///
|
||||
/// This helper traits allows the [`lower_type`] and [`lower_out_type`] methods
|
||||
/// to abstractly lower lifetimes without concern for what sort of tracking
|
||||
/// goes on. In particular, elision inference requires updating internal state
|
||||
/// when visiting lifetimes in the input.
|
||||
pub trait LifetimeLowerer {
|
||||
/// Lowers an [`ast::Lifetime`].
|
||||
fn lower_lifetime(&mut self, lifetime: &ast::Lifetime) -> MaybeStatic<TypeLifetime>;
|
||||
|
||||
/// Lowers a slice of [`ast::Lifetime`]s by calling
|
||||
/// [`LifetimeLowerer::lower_lifetime`] repeatedly.
|
||||
fn lower_lifetimes(&mut self, lifetimes: &[ast::Lifetime]) -> TypeLifetimes {
|
||||
TypeLifetimes::from_fn(lifetimes, |lifetime| self.lower_lifetime(lifetime))
|
||||
}
|
||||
|
||||
/// Lowers a slice of [`ast::Lifetime`], where the strategy may vary depending
|
||||
/// on whether or not the lifetimes are expanded from the `Self` type.
|
||||
///
|
||||
/// The distinction between this and [`LifetimeLowerer::lower_lifetimes`] is
|
||||
/// that if `Self` expands to a type with anonymous lifetimes like `Foo<'_>`,
|
||||
/// then multiple instances of `Self` should expand to have the same anonymous
|
||||
/// lifetime, and this lifetime can be cached inside of the `self` argument.
|
||||
/// Additionally, elision inferences knows to not search inside the generics
|
||||
/// of `Self` types for candidate lifetimes to correspond to elided lifetimes
|
||||
/// in the output.
|
||||
fn lower_generics(&mut self, lifetimes: &[ast::Lifetime], is_self: bool) -> TypeLifetimes;
|
||||
}
|
||||
|
||||
/// A state machine for tracking which lifetime in a function's parameters
|
||||
/// may correspond to elided lifetimes in the output.
|
||||
#[derive(Copy, Clone)]
|
||||
enum ElisionSource {
|
||||
/// No borrows in the input, no elision.
|
||||
NoBorrows,
|
||||
/// `&self` or `&mut self`, elision allowed.
|
||||
SelfParam(MaybeStatic<TypeLifetime>),
|
||||
/// One param contains a borrow, elision allowed.
|
||||
OneParam(MaybeStatic<TypeLifetime>),
|
||||
/// Multiple borrows and no self borrow, no elision.
|
||||
MultipleBorrows,
|
||||
}
|
||||
|
||||
impl ElisionSource {
|
||||
/// Potentially transition to a new state.
|
||||
fn visit_lifetime(&mut self, lifetime: MaybeStatic<TypeLifetime>) {
|
||||
match self {
|
||||
ElisionSource::NoBorrows => *self = ElisionSource::OneParam(lifetime),
|
||||
ElisionSource::SelfParam(_) => {
|
||||
// References to self have the highest precedence, do nothing.
|
||||
}
|
||||
ElisionSource::OneParam(_) => *self = ElisionSource::MultipleBorrows,
|
||||
ElisionSource::MultipleBorrows => {
|
||||
// There's ambiguity. This is valid when there's no elision in
|
||||
// the output.
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// A type for storing shared information between the different states of elision
|
||||
/// inference.
|
||||
///
|
||||
/// This contains data for generating fresh elided lifetimes, looking up named
|
||||
/// lifetimes, and caching lifetimes of `Self`.
|
||||
pub(super) struct BaseLifetimeLowerer<'ast> {
|
||||
lifetime_env: &'ast ast::LifetimeEnv,
|
||||
self_lifetimes: Option<TypeLifetimes>,
|
||||
nodes: SmallVec<[Lifetime; 4]>,
|
||||
num_lifetimes: usize,
|
||||
}
|
||||
|
||||
/// The first phase of output elision inference.
|
||||
///
|
||||
/// In the first phase, the type signature of the `&self` or `&mut self` type
|
||||
/// is lowered into its HIR representation, if present. According to elision
|
||||
/// rules, this reference has the highest precedence as the lifetime that
|
||||
/// goes into elision in the output, and so it's checked first.
|
||||
pub struct SelfParamLifetimeLowerer<'ast> {
|
||||
base: BaseLifetimeLowerer<'ast>,
|
||||
}
|
||||
|
||||
/// The second phase of output elision inference.
|
||||
///
|
||||
/// In the second phase, all lifetimes in the parameter type signatures
|
||||
/// (besides the lifetime of self, if present) are lowered. If a self param
|
||||
/// didn't claim the potential output elided lifetime, then if there's a
|
||||
/// single lifetime (elided or not) in the inputs, it will claim the
|
||||
/// potential output elided lifetime.
|
||||
pub struct ParamLifetimeLowerer<'ast> {
|
||||
elision_source: ElisionSource,
|
||||
base: BaseLifetimeLowerer<'ast>,
|
||||
}
|
||||
|
||||
/// The third and final phase of output elision inference.
|
||||
///
|
||||
/// In the third phase, the type signature of the output type is lowered into
|
||||
/// its HIR representation. If one of the input lifetimes were marked as
|
||||
/// responsible for any elision in the output, then anonymous lifetimes get
|
||||
/// that lifetime. If none did and there is elision in the output, then
|
||||
/// rustc should have errored and said the elision was ambiguous, meaning
|
||||
/// that state should be impossible so it panics.
|
||||
pub struct ReturnLifetimeLowerer<'ast> {
|
||||
elision_source: ElisionSource,
|
||||
base: BaseLifetimeLowerer<'ast>,
|
||||
}
|
||||
|
||||
impl<'ast> BaseLifetimeLowerer<'ast> {
|
||||
/// Returns a [`TypeLifetime`] representing a new anonymous lifetime, and
|
||||
/// pushes it to the nodes vector.
|
||||
fn new_elided(&mut self) -> TypeLifetime {
|
||||
let index = self.num_lifetimes;
|
||||
self.num_lifetimes += 1;
|
||||
TypeLifetime::new(index)
|
||||
}
|
||||
|
||||
/// Lowers a single [`ast::Lifetime`]. If the lifetime is elided, then a fresh
|
||||
/// [`ImplicitLifetime`] is generated.
|
||||
fn lower_lifetime(&mut self, lifetime: &ast::Lifetime) -> MaybeStatic<TypeLifetime> {
|
||||
match lifetime {
|
||||
ast::Lifetime::Static => MaybeStatic::Static,
|
||||
ast::Lifetime::Named(named) => {
|
||||
MaybeStatic::NonStatic(TypeLifetime::from_ast(named, self.lifetime_env))
|
||||
}
|
||||
ast::Lifetime::Anonymous => MaybeStatic::NonStatic(self.new_elided()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieves the cached `Self` lifetimes, or caches newly generated
|
||||
/// lifetimes and returns those.
|
||||
fn self_lifetimes_or_new(&mut self, ast_lifetimes: &[ast::Lifetime]) -> TypeLifetimes {
|
||||
if let Some(lifetimes) = &self.self_lifetimes {
|
||||
lifetimes.clone()
|
||||
} else {
|
||||
let lifetimes = TypeLifetimes::from_fn(ast_lifetimes, |lt| self.lower_lifetime(lt));
|
||||
self.self_lifetimes = Some(lifetimes.clone());
|
||||
lifetimes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast> SelfParamLifetimeLowerer<'ast> {
|
||||
/// Returns a new [`SelfParamLifetimeLowerer`].
|
||||
pub fn new(
|
||||
lifetime_env: &'ast ast::LifetimeEnv,
|
||||
errors: &mut Vec<LoweringError>,
|
||||
) -> Option<Self> {
|
||||
let mut hir_nodes = Some(SmallVec::new());
|
||||
|
||||
for ast_node in lifetime_env.nodes.iter() {
|
||||
let lifetime = lower_ident(ast_node.lifetime.name(), "named lifetime", errors);
|
||||
match (lifetime, &mut hir_nodes) {
|
||||
(Some(lifetime), Some(hir_nodes)) => {
|
||||
hir_nodes.push(Lifetime::new(
|
||||
lifetime,
|
||||
ast_node
|
||||
.longer
|
||||
.iter()
|
||||
.map(|i| MethodLifetime::new(*i))
|
||||
.collect(),
|
||||
ast_node
|
||||
.shorter
|
||||
.iter()
|
||||
.map(|i| MethodLifetime::new(*i))
|
||||
.collect(),
|
||||
));
|
||||
}
|
||||
_ => hir_nodes = None,
|
||||
}
|
||||
}
|
||||
|
||||
hir_nodes.map(|nodes| Self {
|
||||
base: BaseLifetimeLowerer {
|
||||
lifetime_env,
|
||||
self_lifetimes: None,
|
||||
num_lifetimes: nodes.len(),
|
||||
nodes,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/// Lowers the lifetime of `&self` or `&mut self`.
|
||||
///
|
||||
/// The lifetimes of `&self` and `&mut self` are special, because they
|
||||
/// automatically take priority over any other lifetime in the input for
|
||||
/// being tied to any elided lifetimes in the output.
|
||||
///
|
||||
/// Along with returning the lowered lifetime, this method also returns the
|
||||
/// next state in elision inference, the [`ParamLifetimeLowerer`].
|
||||
pub fn lower_self_ref(
|
||||
mut self,
|
||||
lifetime: &ast::Lifetime,
|
||||
) -> (MaybeStatic<TypeLifetime>, ParamLifetimeLowerer<'ast>) {
|
||||
let self_lifetime = self.base.lower_lifetime(lifetime);
|
||||
|
||||
(
|
||||
self_lifetime,
|
||||
self.into_param_ltl(ElisionSource::SelfParam(self_lifetime)),
|
||||
)
|
||||
}
|
||||
|
||||
/// Acknowledges that there's no `&self` or `&mut self`, and transitions
|
||||
/// to the next state, [`ParamLifetimeLowerer`].
|
||||
pub fn no_self_ref(self) -> ParamLifetimeLowerer<'ast> {
|
||||
self.into_param_ltl(ElisionSource::NoBorrows)
|
||||
}
|
||||
|
||||
/// Transition into the next state, [`ParamLifetimeLowerer`].
|
||||
fn into_param_ltl(self, elision_source: ElisionSource) -> ParamLifetimeLowerer<'ast> {
|
||||
ParamLifetimeLowerer {
|
||||
elision_source,
|
||||
base: self.base,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast> ParamLifetimeLowerer<'ast> {
|
||||
/// Once all lifetimes in the parameters are lowered, this function is
|
||||
/// called to transition to the next state, [`ReturnLifetimeLowerer`].
|
||||
pub fn into_return_ltl(self) -> ReturnLifetimeLowerer<'ast> {
|
||||
ReturnLifetimeLowerer {
|
||||
elision_source: self.elision_source,
|
||||
base: self.base,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast> LifetimeLowerer for ParamLifetimeLowerer<'ast> {
|
||||
fn lower_lifetime(&mut self, borrow: &ast::Lifetime) -> MaybeStatic<TypeLifetime> {
|
||||
let lifetime = self.base.lower_lifetime(borrow);
|
||||
self.elision_source.visit_lifetime(lifetime);
|
||||
lifetime
|
||||
}
|
||||
|
||||
fn lower_generics(&mut self, lifetimes: &[ast::Lifetime], is_self: bool) -> TypeLifetimes {
|
||||
if is_self {
|
||||
self.base.self_lifetimes_or_new(lifetimes)
|
||||
} else {
|
||||
self.lower_lifetimes(lifetimes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast> ReturnLifetimeLowerer<'ast> {
|
||||
/// Finalize the lifetimes in the method, returning the resulting [`LifetimeEnv`].
|
||||
pub fn finish(self) -> LifetimeEnv {
|
||||
LifetimeEnv::new(self.base.nodes, self.base.num_lifetimes)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast> LifetimeLowerer for ReturnLifetimeLowerer<'ast> {
|
||||
fn lower_lifetime(&mut self, borrow: &ast::Lifetime) -> MaybeStatic<TypeLifetime> {
|
||||
match borrow {
|
||||
ast::Lifetime::Static => MaybeStatic::Static,
|
||||
ast::Lifetime::Named(named) => {
|
||||
MaybeStatic::NonStatic(TypeLifetime::from_ast(named, self.base.lifetime_env))
|
||||
}
|
||||
ast::Lifetime::Anonymous => match self.elision_source {
|
||||
ElisionSource::SelfParam(lifetime) | ElisionSource::OneParam(lifetime) => lifetime,
|
||||
ElisionSource::NoBorrows => {
|
||||
panic!("nothing to borrow from, this shouldn't pass rustc's checks")
|
||||
}
|
||||
ElisionSource::MultipleBorrows => {
|
||||
panic!("source of elision is ambiguous, this shouldn't pass rustc's checks")
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn lower_generics(&mut self, lifetimes: &[ast::Lifetime], is_self: bool) -> TypeLifetimes {
|
||||
if is_self {
|
||||
self.base.self_lifetimes_or_new(lifetimes)
|
||||
} else {
|
||||
self.lower_lifetimes(lifetimes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LifetimeLowerer for &ast::LifetimeEnv {
|
||||
fn lower_lifetime(&mut self, lifetime: &ast::Lifetime) -> MaybeStatic<TypeLifetime> {
|
||||
match lifetime {
|
||||
ast::Lifetime::Static => MaybeStatic::Static,
|
||||
ast::Lifetime::Named(named) => {
|
||||
MaybeStatic::NonStatic(TypeLifetime::from_ast(named, self))
|
||||
}
|
||||
ast::Lifetime::Anonymous => {
|
||||
panic!("anonymous lifetime inside struct, this shouldn't pass rustc's checks")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn lower_generics(&mut self, lifetimes: &[ast::Lifetime], _: bool) -> TypeLifetimes {
|
||||
self.lower_lifetimes(lifetimes)
|
||||
}
|
||||
}
|
||||
|
||||
// Things to test:
|
||||
// 1. ensure that if there are multiple inputs that are `Self`, where `Self` has
|
||||
// an elided lifetime, all expansions of `Self` have the same anonymous lifetimes.
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use strck_ident::IntoCk;
|
||||
|
||||
/// Convert a syntax tree into a [`TypeContext`].
|
||||
macro_rules! tcx {
|
||||
($($tokens:tt)*) => {{
|
||||
let m = crate::ast::Module::from_syn(&syn::parse_quote! { $($tokens)* }, true);
|
||||
|
||||
let mut env = crate::Env::default();
|
||||
let mut top_symbols = crate::ModuleEnv::default();
|
||||
|
||||
m.insert_all_types(crate::ast::Path::empty(), &mut env);
|
||||
top_symbols.insert(m.name.clone(), crate::ast::ModSymbol::SubModule(m.name.clone()));
|
||||
|
||||
env.insert(crate::ast::Path::empty(), top_symbols);
|
||||
|
||||
let tcx = crate::hir::TypeContext::from_ast(&env).unwrap();
|
||||
|
||||
tcx
|
||||
}}
|
||||
}
|
||||
|
||||
macro_rules! do_test {
|
||||
($($tokens:tt)*) => {{
|
||||
let mut settings = insta::Settings::new();
|
||||
settings.set_sort_maps(true);
|
||||
|
||||
settings.bind(|| {
|
||||
let tcx = tcx! { $($tokens)* };
|
||||
|
||||
insta::assert_debug_snapshot!(tcx);
|
||||
})
|
||||
}}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_mod() {
|
||||
do_test! {
|
||||
mod ffi {
|
||||
#[diplomat::opaque]
|
||||
struct Opaque<'a> {
|
||||
s: &'a str,
|
||||
}
|
||||
|
||||
struct Struct<'a> {
|
||||
s: &'a str,
|
||||
}
|
||||
|
||||
#[diplomat::out]
|
||||
struct OutStruct<'a> {
|
||||
inner: Box<Opaque<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> OutStruct<'a> {
|
||||
pub fn new(s: &'a str) -> Self {
|
||||
Self { inner: Box::new(Opaque { s }) }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl<'a> Struct<'a> {
|
||||
pub fn rustc_elision(self, s: &str) -> &str {
|
||||
s
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_borrowing_fields() {
|
||||
use std::collections::BTreeMap;
|
||||
use std::fmt;
|
||||
|
||||
let tcx = tcx! {
|
||||
mod ffi {
|
||||
#[diplomat::opaque]
|
||||
pub struct Opaque;
|
||||
|
||||
struct Input<'p, 'q> {
|
||||
p_data: &'p Opaque,
|
||||
q_data: &'q Opaque,
|
||||
name: &'static str,
|
||||
inner: Inner<'q>,
|
||||
}
|
||||
|
||||
struct Inner<'a> {
|
||||
more_data: &'a str,
|
||||
}
|
||||
|
||||
struct Output<'p,'q> {
|
||||
p_data: &'p Opaque,
|
||||
q_data: &'q Opaque,
|
||||
}
|
||||
|
||||
impl<'a, 'b> Input<'a, 'b> {
|
||||
pub fn as_output(self, _s: &'static str) -> Output<'b, 'a> {
|
||||
Output { data: self.data }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let method = &tcx
|
||||
.structs()
|
||||
.iter()
|
||||
.find(|def| def.name == "Input")
|
||||
.unwrap()
|
||||
.methods[0];
|
||||
|
||||
let visitor = method.borrowing_field_visitor(&tcx, "this".ck().unwrap());
|
||||
let mut lt_to_borrowing_fields: BTreeMap<_, Vec<_>> = BTreeMap::new();
|
||||
visitor.visit_borrowing_fields(|lt, bf| {
|
||||
lt_to_borrowing_fields
|
||||
.entry(lt)
|
||||
.or_default()
|
||||
.push(DebugBorrowingField(bf));
|
||||
});
|
||||
|
||||
struct DebugBorrowingField<'m>(crate::hir::BorrowingField<'m>);
|
||||
|
||||
impl<'m> fmt::Debug for DebugBorrowingField<'m> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str("\"")?;
|
||||
self.0.try_backtrace(|i, ident| {
|
||||
if i != 0 {
|
||||
f.write_str(".")?;
|
||||
}
|
||||
f.write_str(ident.as_str())
|
||||
})?;
|
||||
f.write_str("\"")
|
||||
}
|
||||
}
|
||||
|
||||
let mut settings = insta::Settings::new();
|
||||
settings.set_sort_maps(true);
|
||||
|
||||
settings.bind(|| {
|
||||
insta::assert_debug_snapshot!(lt_to_borrowing_fields);
|
||||
})
|
||||
}
|
||||
}
|
284
third_party/rust/diplomat_core/src/hir/lifetimes.rs
vendored
Normal file
284
third_party/rust/diplomat_core/src/hir/lifetimes.rs
vendored
Normal file
@ -0,0 +1,284 @@
|
||||
//! Lifetime information for types.
|
||||
#![allow(dead_code)]
|
||||
|
||||
use super::IdentBuf;
|
||||
use crate::ast;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
|
||||
/// Convenience const representing the number of lifetimes a [`LifetimeEnv`]
|
||||
/// can hold inline before needing to dynamically allocate.
|
||||
const INLINE_NUM_LIFETIMES: usize = 4;
|
||||
|
||||
// TODO(Quinn): This type is going to mainly be recycled from `ast::LifetimeEnv`.
|
||||
// Not fully sure how that will look like yet, but the ideas of what this will do
|
||||
// is basically the same.
|
||||
#[derive(Debug)]
|
||||
pub struct LifetimeEnv {
|
||||
/// List of named lifetimes in scope of the method, in the form of an
|
||||
/// adjacency matrix.
|
||||
nodes: SmallVec<[Lifetime; INLINE_NUM_LIFETIMES]>,
|
||||
|
||||
/// The number of named _and_ anonymous lifetimes in the method.
|
||||
/// We store the sum since it represents the upper bound on what indices
|
||||
/// are in range of the graph. If we make a [`MethodLfetimes`] with
|
||||
/// `num_lifetimes` entries, then `TypeLifetime`s that convert into
|
||||
/// `MethodLifetime`s will fall into this range, and we'll know that it's
|
||||
/// a named lifetime if it's < `nodes.len()`, or that it's an anonymous
|
||||
/// lifetime if it's < `num_lifetimes`. Otherwise, we'd have to make a
|
||||
/// distinction in `TypeLifetime` about which kind it refers to.
|
||||
num_lifetimes: usize,
|
||||
}
|
||||
|
||||
/// A lifetime in a [`LifetimeEnv`], which keeps track of which lifetimes it's
|
||||
/// longer and shorter than.
|
||||
#[derive(Debug)]
|
||||
pub(super) struct Lifetime {
|
||||
ident: IdentBuf,
|
||||
longer: SmallVec<[MethodLifetime; 2]>,
|
||||
shorter: SmallVec<[MethodLifetime; 2]>,
|
||||
}
|
||||
|
||||
impl Lifetime {
|
||||
/// Returns a new [`Lifetime`].
|
||||
pub(super) fn new(
|
||||
ident: IdentBuf,
|
||||
longer: SmallVec<[MethodLifetime; 2]>,
|
||||
shorter: SmallVec<[MethodLifetime; 2]>,
|
||||
) -> Self {
|
||||
Self {
|
||||
ident,
|
||||
longer,
|
||||
shorter,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Visit subtype lifetimes recursively, keeping track of which have already
|
||||
/// been visited.
|
||||
pub struct SubtypeLifetimeVisitor<'lt, F> {
|
||||
lifetime_env: &'lt LifetimeEnv,
|
||||
visited: SmallVec<[bool; INLINE_NUM_LIFETIMES]>,
|
||||
visit_fn: F,
|
||||
}
|
||||
|
||||
impl<'lt, F> SubtypeLifetimeVisitor<'lt, F>
|
||||
where
|
||||
F: FnMut(MethodLifetime),
|
||||
{
|
||||
fn new(lifetime_env: &'lt LifetimeEnv, visit_fn: F) -> Self {
|
||||
Self {
|
||||
lifetime_env,
|
||||
visited: smallvec![false; lifetime_env.nodes.len()],
|
||||
visit_fn,
|
||||
}
|
||||
}
|
||||
|
||||
/// Visit more sublifetimes. This method tracks which lifetimes have already
|
||||
/// been visited, and uses this to not visit the same lifetime twice.
|
||||
pub fn visit_subtypes(&mut self, method_lifetime: MethodLifetime) {
|
||||
if let Some(visited @ false) = self.visited.get_mut(method_lifetime.0) {
|
||||
*visited = true;
|
||||
|
||||
(self.visit_fn)(method_lifetime);
|
||||
|
||||
for longer in self.lifetime_env.nodes[method_lifetime.0].longer.iter() {
|
||||
self.visit_subtypes(*longer)
|
||||
}
|
||||
} else {
|
||||
debug_assert!(
|
||||
method_lifetime.0 > self.lifetime_env.num_lifetimes,
|
||||
"method lifetime has an internal index that's not in range of the lifetime env"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper type for `TypeLifetime` and `MethodLifetime`, indicating that it may
|
||||
/// be the `'static` lifetime.
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum MaybeStatic<T> {
|
||||
Static,
|
||||
NonStatic(T),
|
||||
}
|
||||
|
||||
impl<T> MaybeStatic<T> {
|
||||
/// Maps the lifetime, if it's not the `'static` lifetime, to another
|
||||
/// non-static lifetime.
|
||||
pub(super) fn map_nonstatic<F, R>(self, f: F) -> MaybeStatic<R>
|
||||
where
|
||||
F: FnOnce(T) -> R,
|
||||
{
|
||||
match self {
|
||||
MaybeStatic::Static => MaybeStatic::Static,
|
||||
MaybeStatic::NonStatic(lifetime) => MaybeStatic::NonStatic(f(lifetime)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Maps the lifetime, if it's not the `'static` lifetime, to a potentially
|
||||
/// static lifetime.
|
||||
pub(super) fn flat_map_nonstatic<R, F>(self, f: F) -> MaybeStatic<R>
|
||||
where
|
||||
F: FnOnce(T) -> MaybeStatic<R>,
|
||||
{
|
||||
match self {
|
||||
MaybeStatic::Static => MaybeStatic::Static,
|
||||
MaybeStatic::NonStatic(lifetime) => f(lifetime),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A lifetime that exists as part of a type signature.
|
||||
///
|
||||
/// This type can be mapped to a [`MethodLifetime`] by using the
|
||||
/// [`TypeLifetime::as_method_lifetime`] method.
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct TypeLifetime(usize);
|
||||
|
||||
/// A set of lifetimes that exist as generic arguments on [`StructPath`]s,
|
||||
/// [`OutStructPath`]s, and [`OpaquePath`]s.
|
||||
///
|
||||
/// By itself, `TypeLifetimes` isn't very useful. However, it can be combined with
|
||||
/// a [`MethodLifetimes`] using [`TypeLifetimes::as_method_lifetimes`] to get the lifetimes
|
||||
/// in the scope of a method it appears in.
|
||||
///
|
||||
/// [`StructPath`]: super::StructPath
|
||||
/// [`OutStructPath`]: super::OutStructPath
|
||||
/// [`OpaquePath`]: super::OpaquePath
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct TypeLifetimes {
|
||||
indices: SmallVec<[MaybeStatic<TypeLifetime>; 2]>,
|
||||
}
|
||||
|
||||
/// A lifetime that exists as part of a method signature, e.g. `'a` or an
|
||||
/// anonymous lifetime.
|
||||
///
|
||||
/// This type is intended to be used as a key into a map to keep track of which
|
||||
/// borrowed fields depend on which method lifetimes.
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct MethodLifetime(usize);
|
||||
|
||||
/// Map a lifetime in a nested struct to the original lifetime defined
|
||||
/// in the method that it refers to.
|
||||
pub struct MethodLifetimes {
|
||||
indices: SmallVec<[MaybeStatic<MethodLifetime>; 2]>,
|
||||
}
|
||||
|
||||
impl LifetimeEnv {
|
||||
/// Returns a new [`LifetimeEnv`].
|
||||
pub(super) fn new(
|
||||
nodes: SmallVec<[Lifetime; INLINE_NUM_LIFETIMES]>,
|
||||
num_lifetimes: usize,
|
||||
) -> Self {
|
||||
Self {
|
||||
nodes,
|
||||
num_lifetimes,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a fresh [`MethodLifetimes`] corresponding to `self`.
|
||||
pub fn method_lifetimes(&self) -> MethodLifetimes {
|
||||
let indices = (0..self.num_lifetimes)
|
||||
.map(|index| MaybeStatic::NonStatic(MethodLifetime(index)))
|
||||
.collect();
|
||||
|
||||
MethodLifetimes { indices }
|
||||
}
|
||||
|
||||
/// Returns a new [`SubtypeLifetimeVisitor`], which can visit all reachable
|
||||
/// lifetimes
|
||||
pub fn subtype_lifetimes_visitor<F>(&self, visit_fn: F) -> SubtypeLifetimeVisitor<'_, F>
|
||||
where
|
||||
F: FnMut(MethodLifetime),
|
||||
{
|
||||
SubtypeLifetimeVisitor::new(self, visit_fn)
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeLifetime {
|
||||
/// Returns a [`TypeLifetime`] from its AST counterparts.
|
||||
pub(super) fn from_ast(named: &ast::NamedLifetime, lifetime_env: &ast::LifetimeEnv) -> Self {
|
||||
let index = lifetime_env
|
||||
.id(named)
|
||||
.unwrap_or_else(|| panic!("lifetime `{named}` not found in lifetime env"));
|
||||
Self(index)
|
||||
}
|
||||
|
||||
pub(super) fn new(index: usize) -> Self {
|
||||
Self(index)
|
||||
}
|
||||
|
||||
/// Returns a new [`MaybeStatic<MethodLifetime>`] representing `self` in the
|
||||
/// scope of the method that it appears in.
|
||||
///
|
||||
/// For example, if we have some `Foo<'a>` type with a field `&'a Bar`, then
|
||||
/// we can call this on the `'a` on the field. If `Foo` was `Foo<'static>`
|
||||
/// in the method, then this will return `MaybeStatic::Static`. But if it
|
||||
/// was `Foo<'b>`, then this will return `MaybeStatic::NonStatic` containing
|
||||
/// the `MethodLifetime` corresponding to `'b`.
|
||||
pub fn as_method_lifetime(
|
||||
self,
|
||||
method_lifetimes: &MethodLifetimes,
|
||||
) -> MaybeStatic<MethodLifetime> {
|
||||
method_lifetimes.indices[self.0]
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeLifetimes {
|
||||
pub(super) fn from_fn<F>(lifetimes: &[ast::Lifetime], lower_fn: F) -> Self
|
||||
where
|
||||
F: FnMut(&ast::Lifetime) -> MaybeStatic<TypeLifetime>,
|
||||
{
|
||||
Self {
|
||||
indices: lifetimes.iter().map(lower_fn).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a new [`MethodLifetimes`] representing the lifetimes in the scope
|
||||
/// of the method this type appears in.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # struct Alice<'a>(&'a ());
|
||||
/// # struct Bob<'b>(&'b ());
|
||||
/// struct Foo<'a, 'b> {
|
||||
/// alice: Alice<'a>,
|
||||
/// bob: Bob<'b>,
|
||||
/// }
|
||||
///
|
||||
/// fn bar<'x, 'y>(arg: Foo<'x, 'y>) {}
|
||||
/// ```
|
||||
/// Here, `Foo` will have a [`TypeLifetimes`] containing `['a, 'b]`,
|
||||
/// and `bar` will have a [`MethodLifetimes`] containing `{'x: 'x, 'y: 'y}`.
|
||||
/// When we enter the scope of `Foo` as a type, we use this method to combine
|
||||
/// the two to get a new [`MethodLifetimes`] representing the mapping from
|
||||
/// lifetimes in `Foo`'s scope to lifetimes in `bar`s scope: `{'a: 'x, 'b: 'y}`.
|
||||
///
|
||||
/// This tells us that `arg.alice` has lifetime `'x` in the method, and
|
||||
/// that `arg.bob` has lifetime `'y`.
|
||||
pub fn as_method_lifetimes(&self, method_lifetimes: &MethodLifetimes) -> MethodLifetimes {
|
||||
let indices = self
|
||||
.indices
|
||||
.iter()
|
||||
.map(|maybe_static_lt| {
|
||||
maybe_static_lt.flat_map_nonstatic(|lt| lt.as_method_lifetime(method_lifetimes))
|
||||
})
|
||||
.collect();
|
||||
|
||||
MethodLifetimes { indices }
|
||||
}
|
||||
}
|
||||
|
||||
impl MethodLifetime {
|
||||
/// Returns a new `MethodLifetime` from an index into a `LifetimeEnv`.
|
||||
pub(super) fn new(index: usize) -> Self {
|
||||
Self(index)
|
||||
}
|
||||
}
|
||||
|
||||
impl MethodLifetimes {
|
||||
/// Returns an iterator over the contained [`MethodLifetime`]s.
|
||||
pub(super) fn lifetimes(&self) -> impl Iterator<Item = MaybeStatic<MethodLifetime>> + '_ {
|
||||
self.indices.iter().copied()
|
||||
}
|
||||
}
|
856
third_party/rust/diplomat_core/src/hir/lowering.rs
vendored
Normal file
856
third_party/rust/diplomat_core/src/hir/lowering.rs
vendored
Normal file
@ -0,0 +1,856 @@
|
||||
use super::{
|
||||
Borrow, EnumDef, EnumPath, EnumVariant, IdentBuf, LifetimeEnv, LifetimeLowerer, LookupId,
|
||||
MaybeOwn, Method, NonOptional, OpaqueDef, OpaquePath, Optional, OutStructDef, OutStructField,
|
||||
OutStructPath, OutType, Param, ParamLifetimeLowerer, ParamSelf, PrimitiveType,
|
||||
ReturnLifetimeLowerer, ReturnType, ReturnableStructPath, SelfParamLifetimeLowerer, SelfType,
|
||||
Slice, StructDef, StructField, StructPath, SuccessType, Type,
|
||||
};
|
||||
use crate::{ast, Env};
|
||||
use core::fmt;
|
||||
use strck_ident::IntoCk;
|
||||
|
||||
/// An error from lowering the AST to the HIR.
|
||||
#[derive(Debug)]
|
||||
pub enum LoweringError {
|
||||
/// The purpose of having this is that translating to the HIR has enormous
|
||||
/// potential for really detailed error handling and giving suggestions.
|
||||
///
|
||||
/// Unfortunately, working out what the error enum should look like to enable
|
||||
/// this is really hard. The plan is that once the lowering code is completely
|
||||
/// written, we ctrl+F for `"LoweringError::Other"` in the lowering code, and turn every
|
||||
/// instance into an specialized enum variant, generalizing where possible
|
||||
/// without losing any information.
|
||||
Other(String),
|
||||
}
|
||||
|
||||
impl fmt::Display for LoweringError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Self::Other(ref s) => s.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Lowers an [`ast::Ident`]s into an [`hir::IdentBuf`].
|
||||
///
|
||||
/// If there are any errors, they're pushed to `errors` and `None` is returned.
|
||||
pub fn lower_ident(
|
||||
ident: &ast::Ident,
|
||||
context: &'static str,
|
||||
errors: &mut Vec<LoweringError>,
|
||||
) -> Option<IdentBuf> {
|
||||
match ident.as_str().ck() {
|
||||
Ok(name) => Some(name.to_owned()),
|
||||
Err(e) => {
|
||||
errors.push(LoweringError::Other(format!(
|
||||
"Ident `{ident}` from {context} could not be turned into a Rust ident: {e}"
|
||||
)));
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Lowers an AST definition.
|
||||
pub(super) trait TypeLowerer: Sized {
|
||||
/// Type of the AST definition that gets lowered.
|
||||
type AstDef;
|
||||
|
||||
/// Lowers the AST definition into `Self`.
|
||||
///
|
||||
/// If there are any errors, they're pushed to `errors` and `None` is returned.
|
||||
fn lower(
|
||||
ast_def: &Self::AstDef,
|
||||
lookup_id: &LookupId,
|
||||
in_path: &ast::Path,
|
||||
env: &Env,
|
||||
errors: &mut Vec<LoweringError>,
|
||||
) -> Option<Self>;
|
||||
|
||||
/// Lowers multiple items at once
|
||||
fn lower_all(
|
||||
ast_defs: &[(&ast::Path, &Self::AstDef)],
|
||||
lookup_id: &LookupId,
|
||||
env: &Env,
|
||||
errors: &mut Vec<LoweringError>,
|
||||
) -> Option<Vec<Self>> {
|
||||
let mut hir_types = Some(Vec::with_capacity(ast_defs.len()));
|
||||
|
||||
for (in_path, ast_def) in ast_defs {
|
||||
let hir_type = Self::lower(ast_def, lookup_id, in_path, env, errors);
|
||||
|
||||
match (hir_type, &mut hir_types) {
|
||||
(Some(hir_type), Some(hir_types)) => hir_types.push(hir_type),
|
||||
_ => hir_types = None,
|
||||
}
|
||||
}
|
||||
|
||||
hir_types
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeLowerer for EnumDef {
|
||||
type AstDef = ast::Enum;
|
||||
|
||||
fn lower(
|
||||
ast_enum: &Self::AstDef,
|
||||
lookup_id: &LookupId,
|
||||
in_path: &ast::Path,
|
||||
env: &Env,
|
||||
errors: &mut Vec<LoweringError>,
|
||||
) -> Option<Self> {
|
||||
let name = lower_ident(&ast_enum.name, "enum name", errors);
|
||||
|
||||
let mut variants = Some(Vec::with_capacity(ast_enum.variants.len()));
|
||||
|
||||
for (ident, discriminant, docs) in ast_enum.variants.iter() {
|
||||
let name = lower_ident(ident, "enum variant", errors);
|
||||
|
||||
match (name, &mut variants) {
|
||||
(Some(name), Some(variants)) => {
|
||||
variants.push(EnumVariant {
|
||||
docs: docs.clone(),
|
||||
name,
|
||||
discriminant: *discriminant,
|
||||
});
|
||||
}
|
||||
_ => variants = None,
|
||||
}
|
||||
}
|
||||
|
||||
let methods = lower_all_methods(&ast_enum.methods[..], lookup_id, in_path, env, errors);
|
||||
|
||||
Some(EnumDef::new(
|
||||
ast_enum.docs.clone(),
|
||||
name?,
|
||||
variants?,
|
||||
methods?,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeLowerer for OpaqueDef {
|
||||
type AstDef = ast::OpaqueStruct;
|
||||
|
||||
fn lower(
|
||||
ast_opaque: &Self::AstDef,
|
||||
lookup_id: &LookupId,
|
||||
in_path: &ast::Path,
|
||||
env: &Env,
|
||||
errors: &mut Vec<LoweringError>,
|
||||
) -> Option<Self> {
|
||||
let name = lower_ident(&ast_opaque.name, "opaque name", errors);
|
||||
|
||||
let methods = lower_all_methods(&ast_opaque.methods[..], lookup_id, in_path, env, errors);
|
||||
|
||||
Some(OpaqueDef::new(ast_opaque.docs.clone(), name?, methods?))
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeLowerer for StructDef {
|
||||
type AstDef = ast::Struct;
|
||||
|
||||
fn lower(
|
||||
ast_struct: &Self::AstDef,
|
||||
lookup_id: &LookupId,
|
||||
in_path: &ast::Path,
|
||||
env: &Env,
|
||||
errors: &mut Vec<LoweringError>,
|
||||
) -> Option<Self> {
|
||||
let name = lower_ident(&ast_struct.name, "struct name", errors);
|
||||
|
||||
let fields = if ast_struct.fields.is_empty() {
|
||||
errors.push(LoweringError::Other(format!(
|
||||
"struct `{}` is a ZST because it has no fields",
|
||||
ast_struct.name
|
||||
)));
|
||||
None
|
||||
} else {
|
||||
let mut fields = Some(Vec::with_capacity(ast_struct.fields.len()));
|
||||
|
||||
for (name, ty, docs) in ast_struct.fields.iter() {
|
||||
let name = lower_ident(name, "struct field name", errors);
|
||||
let ty = lower_type(
|
||||
ty,
|
||||
Some(&mut &ast_struct.lifetimes),
|
||||
lookup_id,
|
||||
in_path,
|
||||
env,
|
||||
errors,
|
||||
);
|
||||
|
||||
match (name, ty, &mut fields) {
|
||||
(Some(name), Some(ty), Some(fields)) => fields.push(StructField {
|
||||
docs: docs.clone(),
|
||||
name,
|
||||
ty,
|
||||
}),
|
||||
_ => fields = None,
|
||||
}
|
||||
}
|
||||
|
||||
fields
|
||||
};
|
||||
|
||||
let methods = lower_all_methods(&ast_struct.methods[..], lookup_id, in_path, env, errors);
|
||||
|
||||
Some(StructDef::new(
|
||||
ast_struct.docs.clone(),
|
||||
name?,
|
||||
fields?,
|
||||
methods?,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeLowerer for OutStructDef {
|
||||
type AstDef = ast::Struct;
|
||||
|
||||
fn lower(
|
||||
ast_out_struct: &Self::AstDef,
|
||||
lookup_id: &LookupId,
|
||||
in_path: &ast::Path,
|
||||
env: &Env,
|
||||
errors: &mut Vec<LoweringError>,
|
||||
) -> Option<Self> {
|
||||
let name = lower_ident(&ast_out_struct.name, "out-struct name", errors);
|
||||
|
||||
let fields = if ast_out_struct.fields.is_empty() {
|
||||
errors.push(LoweringError::Other(format!(
|
||||
"struct `{}` is a ZST because it has no fields",
|
||||
ast_out_struct.name
|
||||
)));
|
||||
None
|
||||
} else {
|
||||
let mut fields = Some(Vec::with_capacity(ast_out_struct.fields.len()));
|
||||
|
||||
for (name, ty, docs) in ast_out_struct.fields.iter() {
|
||||
let name = lower_ident(name, "out-struct field name", errors);
|
||||
let ty = lower_out_type(
|
||||
ty,
|
||||
Some(&mut &ast_out_struct.lifetimes),
|
||||
lookup_id,
|
||||
in_path,
|
||||
env,
|
||||
errors,
|
||||
);
|
||||
|
||||
match (name, ty, &mut fields) {
|
||||
(Some(name), Some(ty), Some(fields)) => fields.push(OutStructField {
|
||||
docs: docs.clone(),
|
||||
name,
|
||||
ty,
|
||||
}),
|
||||
_ => fields = None,
|
||||
}
|
||||
}
|
||||
|
||||
fields
|
||||
};
|
||||
|
||||
let methods =
|
||||
lower_all_methods(&ast_out_struct.methods[..], lookup_id, in_path, env, errors);
|
||||
|
||||
Some(OutStructDef::new(
|
||||
ast_out_struct.docs.clone(),
|
||||
name?,
|
||||
fields?,
|
||||
methods?,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// Lowers an [`ast::Method`]s an [`hir::Method`].
|
||||
///
|
||||
/// If there are any errors, they're pushed to `errors` and `None` is returned.
|
||||
fn lower_method(
|
||||
method: &ast::Method,
|
||||
lookup_id: &LookupId,
|
||||
in_path: &ast::Path,
|
||||
env: &Env,
|
||||
errors: &mut Vec<LoweringError>,
|
||||
) -> Option<Method> {
|
||||
let name = lower_ident(&method.name, "method name", errors);
|
||||
|
||||
let (ast_params, takes_writeable) = match method.params.split_last() {
|
||||
Some((last, remaining)) if last.is_writeable() => (remaining, true),
|
||||
_ => (&method.params[..], false),
|
||||
};
|
||||
|
||||
let self_param_ltl = SelfParamLifetimeLowerer::new(&method.lifetime_env, errors);
|
||||
|
||||
let (param_self, param_ltl) = if let Some(self_param) = method.self_param.as_ref() {
|
||||
lower_self_param(
|
||||
self_param,
|
||||
self_param_ltl,
|
||||
lookup_id,
|
||||
&method.full_path_name,
|
||||
in_path,
|
||||
env,
|
||||
errors,
|
||||
)
|
||||
.map(|(param_self, param_ltl)| (Some(Some(param_self)), Some(param_ltl)))
|
||||
.unwrap_or((None, None))
|
||||
} else {
|
||||
(
|
||||
Some(None),
|
||||
self_param_ltl.map(SelfParamLifetimeLowerer::no_self_ref),
|
||||
)
|
||||
};
|
||||
|
||||
let (params, return_ltl) =
|
||||
lower_many_params(ast_params, param_ltl, lookup_id, in_path, env, errors)
|
||||
.map(|(params, return_ltl)| (Some(params), Some(return_ltl)))
|
||||
.unwrap_or((None, None));
|
||||
|
||||
let (output, lifetime_env) = lower_return_type(
|
||||
method.return_type.as_ref(),
|
||||
takes_writeable,
|
||||
return_ltl,
|
||||
lookup_id,
|
||||
in_path,
|
||||
env,
|
||||
errors,
|
||||
)?;
|
||||
|
||||
Some(Method {
|
||||
docs: method.docs.clone(),
|
||||
name: name?,
|
||||
lifetime_env,
|
||||
param_self: param_self?,
|
||||
params: params?,
|
||||
output,
|
||||
})
|
||||
}
|
||||
|
||||
/// Lowers many [`ast::Method`]s into a vector of [`hir::Method`]s.
|
||||
///
|
||||
/// If there are any errors, they're pushed to `errors` and `None` is returned.
|
||||
fn lower_all_methods(
|
||||
ast_methods: &[ast::Method],
|
||||
lookup_id: &LookupId,
|
||||
in_path: &ast::Path,
|
||||
env: &Env,
|
||||
errors: &mut Vec<LoweringError>,
|
||||
) -> Option<Vec<Method>> {
|
||||
let mut methods = Some(Vec::with_capacity(ast_methods.len()));
|
||||
|
||||
for method in ast_methods {
|
||||
let method = lower_method(method, lookup_id, in_path, env, errors);
|
||||
match (method, &mut methods) {
|
||||
(Some(method), Some(methods)) => {
|
||||
methods.push(method);
|
||||
}
|
||||
_ => methods = None,
|
||||
}
|
||||
}
|
||||
|
||||
methods
|
||||
}
|
||||
|
||||
/// Lowers an [`ast::TypeName`]s into a [`hir::Type`].
|
||||
///
|
||||
/// If there are any errors, they're pushed to `errors` and `None` is returned.
|
||||
fn lower_type<L: LifetimeLowerer>(
|
||||
ty: &ast::TypeName,
|
||||
ltl: Option<&mut L>,
|
||||
lookup_id: &LookupId,
|
||||
in_path: &ast::Path,
|
||||
env: &Env,
|
||||
errors: &mut Vec<LoweringError>,
|
||||
) -> Option<Type> {
|
||||
match ty {
|
||||
ast::TypeName::Primitive(prim) => Some(Type::Primitive(PrimitiveType::from_ast(*prim))),
|
||||
ast::TypeName::Named(path) | ast::TypeName::SelfType(path) => {
|
||||
match path.resolve(in_path, env) {
|
||||
ast::CustomType::Struct(strct) => {
|
||||
if let Some(tcx_id) = lookup_id.resolve_struct(strct) {
|
||||
let lifetimes = ltl?.lower_generics(&path.lifetimes[..], ty.is_self());
|
||||
|
||||
Some(Type::Struct(StructPath::new(lifetimes, tcx_id)))
|
||||
} else if lookup_id.resolve_out_struct(strct).is_some() {
|
||||
errors.push(LoweringError::Other(format!("found struct in input that is marked with #[diplomat::out]: {ty} in {path}")));
|
||||
None
|
||||
} else {
|
||||
unreachable!("struct `{}` wasn't found in the set of structs or out-structs, this is a bug.", strct.name);
|
||||
}
|
||||
}
|
||||
ast::CustomType::Opaque(_) => {
|
||||
errors.push(LoweringError::Other(format!(
|
||||
"Opaque passed by value in input: {path}"
|
||||
)));
|
||||
None
|
||||
}
|
||||
ast::CustomType::Enum(enm) => {
|
||||
let tcx_id = lookup_id
|
||||
.resolve_enum(enm)
|
||||
.expect("can't find enum in lookup map, which contains all enums from env");
|
||||
|
||||
Some(Type::Enum(EnumPath::new(tcx_id)))
|
||||
}
|
||||
}
|
||||
}
|
||||
ast::TypeName::Reference(lifetime, mutability, ref_ty) => match ref_ty.as_ref() {
|
||||
ast::TypeName::Named(path) | ast::TypeName::SelfType(path) => {
|
||||
match path.resolve(in_path, env) {
|
||||
ast::CustomType::Opaque(opaque) => ltl.map(|ltl| {
|
||||
let borrow = Borrow::new(ltl.lower_lifetime(lifetime), *mutability);
|
||||
let lifetimes = ltl.lower_generics(&path.lifetimes[..], ref_ty.is_self());
|
||||
let tcx_id = lookup_id.resolve_opaque(opaque).expect(
|
||||
"can't find opaque in lookup map, which contains all opaques from env",
|
||||
);
|
||||
|
||||
Type::Opaque(OpaquePath::new(lifetimes, Optional(false), borrow, tcx_id))
|
||||
}),
|
||||
_ => {
|
||||
errors.push(LoweringError::Other(format!("found &T in input where T is a custom type, but not opaque. T = {ref_ty}")));
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
errors.push(LoweringError::Other(format!("found &T in input where T isn't a custom type and therefore not opaque. T = {ref_ty}")));
|
||||
None
|
||||
}
|
||||
},
|
||||
ast::TypeName::Box(box_ty) => {
|
||||
errors.push(match box_ty.as_ref() {
|
||||
ast::TypeName::Named(path) | ast::TypeName::SelfType(path) => {
|
||||
match path.resolve(in_path, env) {
|
||||
ast::CustomType::Opaque(_) => LoweringError::Other(format!("found Box<T> in input where T is an opaque, but owned opaques aren't allowed in inputs. try &T instead? T = {path}")),
|
||||
_ => LoweringError::Other(format!("found Box<T> in input where T is a custom type but not opaque. non-opaques can't be behind pointers, and opaques in inputs can't be owned. T = {path}")),
|
||||
}
|
||||
}
|
||||
_ => LoweringError::Other(format!("found Box<T> in input where T isn't a custom type. T = {box_ty}")),
|
||||
});
|
||||
None
|
||||
}
|
||||
ast::TypeName::Option(opt_ty) => {
|
||||
match opt_ty.as_ref() {
|
||||
ast::TypeName::Reference(lifetime, mutability, ref_ty) => match ref_ty.as_ref() {
|
||||
ast::TypeName::Named(path) | ast::TypeName::SelfType(path) => match path.resolve(in_path, env) {
|
||||
ast::CustomType::Opaque(opaque) => ltl.map(|ltl| {
|
||||
let borrow = Borrow::new(ltl.lower_lifetime(lifetime), *mutability);
|
||||
let lifetimes = ltl.lower_generics(&path.lifetimes, ref_ty.is_self());
|
||||
let tcx_id = lookup_id.resolve_opaque(opaque).expect(
|
||||
"can't find opaque in lookup map, which contains all opaques from env",
|
||||
);
|
||||
|
||||
Type::Opaque(OpaquePath::new(
|
||||
lifetimes,
|
||||
Optional(true),
|
||||
borrow,
|
||||
tcx_id,
|
||||
))
|
||||
}),
|
||||
_ => {
|
||||
errors.push(LoweringError::Other(format!("found Option<&T> in input where T is a custom type, but it's not opaque. T = {ref_ty}")));
|
||||
None
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
errors.push(LoweringError::Other(format!("found Option<&T> in input, but T isn't a custom type and therefore not opaque. T = {ref_ty}")));
|
||||
None
|
||||
}
|
||||
},
|
||||
ast::TypeName::Box(box_ty) => {
|
||||
// we could see whats in the box here too
|
||||
errors.push(LoweringError::Other(format!("found Option<Box<T>> in input, but box isn't allowed in inputs. T = {box_ty}")));
|
||||
None
|
||||
}
|
||||
_ => {
|
||||
errors.push(LoweringError::Other(format!("found Option<T> in input, where T isn't a reference but Option<T> in inputs requires that T is a reference to an opaque. T = {opt_ty}")));
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
ast::TypeName::Result(_, _, _) => {
|
||||
errors.push(LoweringError::Other(
|
||||
"Results can only appear as the top-level return type of methods".into(),
|
||||
));
|
||||
None
|
||||
}
|
||||
ast::TypeName::Writeable => {
|
||||
errors.push(LoweringError::Other(
|
||||
"Writeables can only appear as the last parameter of a method".into(),
|
||||
));
|
||||
None
|
||||
}
|
||||
ast::TypeName::StrReference(lifetime) => {
|
||||
Some(Type::Slice(Slice::Str(ltl?.lower_lifetime(lifetime))))
|
||||
}
|
||||
ast::TypeName::PrimitiveSlice(lifetime, mutability, prim) => {
|
||||
let borrow = Borrow::new(ltl?.lower_lifetime(lifetime), *mutability);
|
||||
let prim = PrimitiveType::from_ast(*prim);
|
||||
|
||||
Some(Type::Slice(Slice::Primitive(borrow, prim)))
|
||||
}
|
||||
ast::TypeName::Unit => {
|
||||
errors.push(LoweringError::Other("Unit types can only appear as the return value of a method, or as the Ok/Err variants of a returned result".into()));
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Lowers an [`ast::TypeName`]s into an [`hir::OutType`].
|
||||
///
|
||||
/// If there are any errors, they're pushed to `errors` and `None` is returned.
|
||||
fn lower_out_type<L: LifetimeLowerer>(
|
||||
ty: &ast::TypeName,
|
||||
ltl: Option<&mut L>,
|
||||
lookup_id: &LookupId,
|
||||
in_path: &ast::Path,
|
||||
env: &Env,
|
||||
errors: &mut Vec<LoweringError>,
|
||||
) -> Option<OutType> {
|
||||
match ty {
|
||||
ast::TypeName::Primitive(prim) => Some(OutType::Primitive(PrimitiveType::from_ast(*prim))),
|
||||
ast::TypeName::Named(path) | ast::TypeName::SelfType(path) => {
|
||||
match path.resolve(in_path, env) {
|
||||
ast::CustomType::Struct(strct) => {
|
||||
let lifetimes = ltl?.lower_generics(&path.lifetimes, ty.is_self());
|
||||
|
||||
if let Some(tcx_id) = lookup_id.resolve_struct(strct) {
|
||||
Some(OutType::Struct(ReturnableStructPath::Struct(
|
||||
StructPath::new(lifetimes, tcx_id),
|
||||
)))
|
||||
} else if let Some(tcx_id) = lookup_id.resolve_out_struct(strct) {
|
||||
Some(OutType::Struct(ReturnableStructPath::OutStruct(
|
||||
OutStructPath::new(lifetimes, tcx_id),
|
||||
)))
|
||||
} else {
|
||||
unreachable!("struct `{}` wasn't found in the set of structs or out-structs, this is a bug.", strct.name);
|
||||
}
|
||||
}
|
||||
ast::CustomType::Opaque(_) => {
|
||||
errors.push(LoweringError::Other(format!(
|
||||
"Opaque passed by value in input: {path}"
|
||||
)));
|
||||
None
|
||||
}
|
||||
ast::CustomType::Enum(enm) => {
|
||||
let tcx_id = lookup_id
|
||||
.resolve_enum(enm)
|
||||
.expect("can't find enum in lookup map, which contains all enums from env");
|
||||
|
||||
Some(OutType::Enum(EnumPath::new(tcx_id)))
|
||||
}
|
||||
}
|
||||
}
|
||||
ast::TypeName::Reference(lifetime, mutability, ref_ty) => match ref_ty.as_ref() {
|
||||
ast::TypeName::Named(path) | ast::TypeName::SelfType(path) => {
|
||||
match path.resolve(in_path, env) {
|
||||
ast::CustomType::Opaque(opaque) => ltl.map(|ltl| {
|
||||
let borrow = Borrow::new(ltl.lower_lifetime(lifetime), *mutability);
|
||||
let lifetimes = ltl.lower_generics(&path.lifetimes, ref_ty.is_self());
|
||||
let tcx_id = lookup_id.resolve_opaque(opaque).expect(
|
||||
"can't find opaque in lookup map, which contains all opaques from env",
|
||||
);
|
||||
|
||||
OutType::Opaque(OpaquePath::new(
|
||||
lifetimes,
|
||||
Optional(false),
|
||||
MaybeOwn::Borrow(borrow),
|
||||
tcx_id,
|
||||
))
|
||||
}),
|
||||
_ => {
|
||||
errors.push(LoweringError::Other(format!("found &T in output where T is a custom type, but not opaque. T = {ref_ty}")));
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
errors.push(LoweringError::Other(format!("found &T in output where T isn't a custom type and therefore not opaque. T = {ref_ty}")));
|
||||
None
|
||||
}
|
||||
},
|
||||
ast::TypeName::Box(box_ty) => match box_ty.as_ref() {
|
||||
ast::TypeName::Named(path) | ast::TypeName::SelfType(path) => {
|
||||
match path.resolve(in_path, env) {
|
||||
ast::CustomType::Opaque(opaque) => ltl.map(|ltl| {
|
||||
let lifetimes = ltl.lower_generics(&path.lifetimes, box_ty.is_self());
|
||||
let tcx_id = lookup_id.resolve_opaque(opaque).expect(
|
||||
"can't find opaque in lookup map, which contains all opaques from env",
|
||||
);
|
||||
|
||||
OutType::Opaque(OpaquePath::new(
|
||||
lifetimes,
|
||||
Optional(true),
|
||||
MaybeOwn::Own,
|
||||
tcx_id,
|
||||
))
|
||||
}),
|
||||
_ => {
|
||||
errors.push(LoweringError::Other(format!("found Box<T> in output where T is a custom type but not opaque. non-opaques can't be behind pointers. T = {path}")));
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
errors.push(LoweringError::Other(format!(
|
||||
"found Box<T> in output where T isn't a custom type. T = {box_ty}"
|
||||
)));
|
||||
None
|
||||
}
|
||||
},
|
||||
ast::TypeName::Option(opt_ty) => match opt_ty.as_ref() {
|
||||
ast::TypeName::Reference(lifetime, mutability, ref_ty) => match ref_ty.as_ref() {
|
||||
ast::TypeName::Named(path) | ast::TypeName::SelfType(path) => {
|
||||
match path.resolve(in_path, env) {
|
||||
ast::CustomType::Opaque(opaque) => ltl.map(|ltl| {
|
||||
let borrow = Borrow::new(ltl.lower_lifetime(lifetime), *mutability);
|
||||
let lifetimes = ltl.lower_generics(&path.lifetimes, ref_ty.is_self());
|
||||
let tcx_id = lookup_id.resolve_opaque(opaque).expect(
|
||||
"can't find opaque in lookup map, which contains all opaques from env",
|
||||
);
|
||||
|
||||
OutType::Opaque(OpaquePath::new(
|
||||
lifetimes,
|
||||
Optional(true),
|
||||
MaybeOwn::Borrow(borrow),
|
||||
tcx_id,
|
||||
))
|
||||
}),
|
||||
_ => {
|
||||
errors.push(LoweringError::Other(format!("found Option<&T> where T is a custom type, but it's not opaque. T = {ref_ty}")));
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
errors.push(LoweringError::Other(format!("found Option<&T>, but T isn't a custom type and therefore not opaque. T = {ref_ty}")));
|
||||
None
|
||||
}
|
||||
},
|
||||
ast::TypeName::Box(box_ty) => match box_ty.as_ref() {
|
||||
ast::TypeName::Named(path) | ast::TypeName::SelfType(path) => {
|
||||
match path.resolve(in_path, env) {
|
||||
ast::CustomType::Opaque(opaque) => {
|
||||
let lifetimes = ltl?.lower_generics(&path.lifetimes, box_ty.is_self());
|
||||
let tcx_id = lookup_id.resolve_opaque(opaque).expect(
|
||||
"can't find opaque in lookup map, which contains all opaques from env",
|
||||
);
|
||||
|
||||
Some(OutType::Opaque(OpaquePath::new(
|
||||
lifetimes,
|
||||
Optional(true),
|
||||
MaybeOwn::Own,
|
||||
tcx_id,
|
||||
)))
|
||||
}
|
||||
_ => {
|
||||
errors.push(LoweringError::Other(format!("found Option<Box<T>> where T is a custom type, but it's not opaque. T = {box_ty}")));
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
errors.push(LoweringError::Other(format!("found Option<Box<T>>, but T isn't a custom type and therefore not opaque. T = {box_ty}")));
|
||||
None
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
errors.push(LoweringError::Other(format!("found Option<T>, where T isn't a reference but Option<T> in inputs requires that T is a reference to an opaque. T = {opt_ty}")));
|
||||
None
|
||||
}
|
||||
},
|
||||
ast::TypeName::Result(_, _, _) => {
|
||||
errors.push(LoweringError::Other(
|
||||
"Results can only appear as the top-level return type of methods".into(),
|
||||
));
|
||||
None
|
||||
}
|
||||
ast::TypeName::Writeable => {
|
||||
errors.push(LoweringError::Other(
|
||||
"Writeables can only appear as the last parameter of a method".into(),
|
||||
));
|
||||
None
|
||||
}
|
||||
ast::TypeName::StrReference(lifetime) => {
|
||||
Some(OutType::Slice(Slice::Str(ltl?.lower_lifetime(lifetime))))
|
||||
}
|
||||
ast::TypeName::PrimitiveSlice(lifetime, mutability, prim) => {
|
||||
let borrow = Borrow::new(ltl?.lower_lifetime(lifetime), *mutability);
|
||||
let prim = PrimitiveType::from_ast(*prim);
|
||||
|
||||
Some(OutType::Slice(Slice::Primitive(borrow, prim)))
|
||||
}
|
||||
ast::TypeName::Unit => {
|
||||
errors.push(LoweringError::Other("Unit types can only appear as the return value of a method, or as the Ok/Err variants of a returned result".into()));
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Lowers an [`ast::SelfParam`] into an [`hir::ParamSelf`].
|
||||
///
|
||||
/// If there are any errors, they're pushed to `errors` and `None` is returned.
|
||||
fn lower_self_param<'ast>(
|
||||
self_param: &ast::SelfParam,
|
||||
self_param_ltl: Option<SelfParamLifetimeLowerer<'ast>>,
|
||||
lookup_id: &LookupId,
|
||||
method_full_path: &ast::Ident, // for better error msg
|
||||
in_path: &ast::Path,
|
||||
env: &Env,
|
||||
errors: &mut Vec<LoweringError>,
|
||||
) -> Option<(ParamSelf, ParamLifetimeLowerer<'ast>)> {
|
||||
match self_param.path_type.resolve(in_path, env) {
|
||||
ast::CustomType::Struct(strct) => {
|
||||
if let Some(tcx_id) = lookup_id.resolve_struct(strct) {
|
||||
if self_param.reference.is_some() {
|
||||
errors.push(LoweringError::Other(format!("Method `{method_full_path}` takes a reference to a struct as a self parameter, which isn't allowed")));
|
||||
None
|
||||
} else {
|
||||
let mut param_ltl = self_param_ltl?.no_self_ref();
|
||||
|
||||
// Even if we explicitly write out the type of `self` like
|
||||
// `self: Foo<'a>`, the `'a` is still not considered for
|
||||
// elision according to rustc, so is_self=true.
|
||||
let type_lifetimes =
|
||||
param_ltl.lower_generics(&self_param.path_type.lifetimes[..], true);
|
||||
|
||||
Some((
|
||||
ParamSelf::new(SelfType::Struct(StructPath::new(type_lifetimes, tcx_id))),
|
||||
param_ltl,
|
||||
))
|
||||
}
|
||||
} else if lookup_id.resolve_out_struct(strct).is_some() {
|
||||
if let Some((lifetime, _)) = &self_param.reference {
|
||||
errors.push(LoweringError::Other(format!("Method `{method_full_path}` takes an out-struct as the self parameter, which isn't allowed. Also, it's behind a reference, `{lifetime}`, but only opaques can be behind references")));
|
||||
None
|
||||
} else {
|
||||
errors.push(LoweringError::Other(format!("Method `{method_full_path}` takes an out-struct as the self parameter, which isn't allowed")));
|
||||
None
|
||||
}
|
||||
} else {
|
||||
unreachable!(
|
||||
"struct `{}` wasn't found in the set of structs or out-structs, this is a bug.",
|
||||
strct.name
|
||||
);
|
||||
}
|
||||
}
|
||||
ast::CustomType::Opaque(opaque) => {
|
||||
let tcx_id = lookup_id.resolve_opaque(opaque).expect("opaque is in env");
|
||||
|
||||
if let Some((lifetime, mutability)) = &self_param.reference {
|
||||
let (borrow_lifetime, mut param_ltl) = self_param_ltl?.lower_self_ref(lifetime);
|
||||
let borrow = Borrow::new(borrow_lifetime, *mutability);
|
||||
let lifetimes = param_ltl.lower_generics(&self_param.path_type.lifetimes, true);
|
||||
|
||||
Some((
|
||||
ParamSelf::new(SelfType::Opaque(OpaquePath::new(
|
||||
lifetimes,
|
||||
NonOptional,
|
||||
borrow,
|
||||
tcx_id,
|
||||
))),
|
||||
param_ltl,
|
||||
))
|
||||
} else {
|
||||
errors.push(LoweringError::Other(format!("Method `{method_full_path}` takes an opaque by value as the self parameter, but opaques as inputs must be behind refs")));
|
||||
None
|
||||
}
|
||||
}
|
||||
ast::CustomType::Enum(enm) => {
|
||||
let tcx_id = lookup_id.resolve_enum(enm).expect("enum is in env");
|
||||
|
||||
Some((
|
||||
ParamSelf::new(SelfType::Enum(EnumPath::new(tcx_id))),
|
||||
self_param_ltl?.no_self_ref(),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Lowers an [`ast::Param`] into an [`hir::Param`].
|
||||
///
|
||||
/// If there are any errors, they're pushed to `errors` and `None` is returned.
|
||||
///
|
||||
/// Note that this expects that if there was a writeable param at the end in
|
||||
/// the method, it's not passed into here.
|
||||
fn lower_param<L: LifetimeLowerer>(
|
||||
param: &ast::Param,
|
||||
ltl: Option<&mut L>,
|
||||
lookup_id: &LookupId,
|
||||
in_path: &ast::Path,
|
||||
env: &Env,
|
||||
errors: &mut Vec<LoweringError>,
|
||||
) -> Option<Param> {
|
||||
let name = lower_ident(¶m.name, "param name", errors);
|
||||
let ty = lower_type(¶m.ty, ltl, lookup_id, in_path, env, errors);
|
||||
|
||||
Some(Param::new(name?, ty?))
|
||||
}
|
||||
|
||||
/// Lowers many [`ast::Param`]s into a vector of [`hir::Param`]s.
|
||||
///
|
||||
/// If there are any errors, they're pushed to `errors` and `None` is returned.
|
||||
///
|
||||
/// Note that this expects that if there was a writeable param at the end in
|
||||
/// the method, `ast_params` was sliced to not include it. This happens in
|
||||
/// `lower_method`, the caller of this function.
|
||||
fn lower_many_params<'ast>(
|
||||
ast_params: &[ast::Param],
|
||||
mut param_ltl: Option<ParamLifetimeLowerer<'ast>>,
|
||||
lookup_id: &LookupId,
|
||||
in_path: &ast::Path,
|
||||
env: &Env,
|
||||
errors: &mut Vec<LoweringError>,
|
||||
) -> Option<(Vec<Param>, ReturnLifetimeLowerer<'ast>)> {
|
||||
let mut params = Some(Vec::with_capacity(ast_params.len()));
|
||||
|
||||
for param in ast_params {
|
||||
let param = lower_param(param, param_ltl.as_mut(), lookup_id, in_path, env, errors);
|
||||
|
||||
match (param, &mut params) {
|
||||
(Some(param), Some(params)) => {
|
||||
params.push(param);
|
||||
}
|
||||
_ => params = None,
|
||||
}
|
||||
}
|
||||
|
||||
Some((params?, param_ltl?.into_return_ltl()))
|
||||
}
|
||||
|
||||
/// Lowers the return type of an [`ast::Method`] into a [`hir::ReturnFallability`].
|
||||
///
|
||||
/// If there are any errors, they're pushed to `errors` and `None` is returned.
|
||||
fn lower_return_type(
|
||||
return_type: Option<&ast::TypeName>,
|
||||
takes_writeable: bool,
|
||||
mut return_ltl: Option<ReturnLifetimeLowerer<'_>>,
|
||||
lookup_id: &LookupId,
|
||||
in_path: &ast::Path,
|
||||
env: &Env,
|
||||
errors: &mut Vec<LoweringError>,
|
||||
) -> Option<(ReturnType, LifetimeEnv)> {
|
||||
let writeable_option = if takes_writeable {
|
||||
Some(SuccessType::Writeable)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
match return_type.unwrap_or(&ast::TypeName::Unit) {
|
||||
ast::TypeName::Result(ok_ty, err_ty, _) => {
|
||||
let ok_ty = match ok_ty.as_ref() {
|
||||
ast::TypeName::Unit => Some(writeable_option),
|
||||
ty => lower_out_type(ty, return_ltl.as_mut(), lookup_id, in_path, env, errors)
|
||||
.map(|ty| Some(SuccessType::OutType(ty))),
|
||||
};
|
||||
let err_ty = match err_ty.as_ref() {
|
||||
ast::TypeName::Unit => Some(None),
|
||||
ty => lower_out_type(ty, return_ltl.as_mut(), lookup_id, in_path, env, errors)
|
||||
.map(Some),
|
||||
};
|
||||
|
||||
match (ok_ty, err_ty) {
|
||||
(Some(ok_ty), Some(err_ty)) => Some(ReturnType::Fallible(ok_ty, err_ty)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
ast::TypeName::Unit => Some(ReturnType::Infallible(writeable_option)),
|
||||
ty => lower_out_type(ty, return_ltl.as_mut(), lookup_id, in_path, env, errors)
|
||||
.map(|ty| ReturnType::Infallible(Some(SuccessType::OutType(ty)))),
|
||||
}
|
||||
.and_then(|return_fallability| Some((return_fallability, return_ltl?.finish())))
|
||||
}
|
489
third_party/rust/diplomat_core/src/hir/methods.rs
vendored
Normal file
489
third_party/rust/diplomat_core/src/hir/methods.rs
vendored
Normal file
@ -0,0 +1,489 @@
|
||||
//! Methods for types and navigating lifetimes within methods.
|
||||
|
||||
use std::fmt::{self, Write};
|
||||
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use super::{
|
||||
paths, Docs, Ident, IdentBuf, LifetimeEnv, MaybeStatic, MethodLifetime, MethodLifetimes,
|
||||
OutType, SelfType, Slice, Type, TypeContext, TypeLifetime, TypeLifetimes,
|
||||
};
|
||||
|
||||
/// A method exposed to Diplomat.
|
||||
#[derive(Debug)]
|
||||
pub struct Method {
|
||||
pub docs: Docs,
|
||||
pub name: IdentBuf,
|
||||
pub lifetime_env: LifetimeEnv,
|
||||
|
||||
pub param_self: Option<ParamSelf>,
|
||||
pub params: Vec<Param>,
|
||||
pub output: ReturnType,
|
||||
}
|
||||
|
||||
/// Type that the method returns.
|
||||
#[derive(Debug)]
|
||||
pub enum SuccessType {
|
||||
Writeable,
|
||||
OutType(OutType),
|
||||
}
|
||||
|
||||
/// Whether or not the method returns a value or a result.
|
||||
#[derive(Debug)]
|
||||
pub enum ReturnType {
|
||||
Infallible(Option<SuccessType>),
|
||||
Fallible(Option<SuccessType>, Option<OutType>),
|
||||
}
|
||||
|
||||
/// The `self` parameter of a method.
|
||||
#[derive(Debug)]
|
||||
pub struct ParamSelf {
|
||||
pub ty: SelfType,
|
||||
}
|
||||
|
||||
/// A parameter in a method.
|
||||
#[derive(Debug)]
|
||||
pub struct Param {
|
||||
pub name: IdentBuf,
|
||||
pub ty: Type,
|
||||
}
|
||||
|
||||
/// An id for indexing into a [`BorrowingFieldsVisitor`].
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
struct ParentId(usize);
|
||||
|
||||
/// Convenience const representing the number of nested structs a [`BorrowingFieldVisitor`]
|
||||
/// can hold inline before needing to dynamically allocate.
|
||||
const INLINE_NUM_PARENTS: usize = 4;
|
||||
|
||||
/// Convenience const representing the number of borrowed fields a [`BorrowingFieldVisitor`]
|
||||
/// can hold inline before needing to dynamically allocate.
|
||||
const INLINE_NUM_LEAVES: usize = 8;
|
||||
|
||||
/// A tree of lifetimes mapping onto a specific instantiation of a type tree.
|
||||
///
|
||||
/// Each `BorrowingFieldsVisitor` corresponds to the type of an input of a method.
|
||||
pub struct BorrowingFieldVisitor<'m> {
|
||||
parents: SmallVec<[(Option<ParentId>, &'m Ident); INLINE_NUM_PARENTS]>,
|
||||
leaves: SmallVec<[BorrowingFieldVisitorLeaf; INLINE_NUM_LEAVES]>,
|
||||
}
|
||||
|
||||
/// Non-recursive input-output types that contain lifetimes
|
||||
enum BorrowingFieldVisitorLeaf {
|
||||
Opaque(ParentId, MaybeStatic<MethodLifetime>, MethodLifetimes),
|
||||
Slice(ParentId, MaybeStatic<MethodLifetime>),
|
||||
}
|
||||
|
||||
/// A leaf of a lifetime tree capable of tracking its parents.
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct BorrowingField<'m> {
|
||||
/// All inner nodes in the tree. When tracing from the root, we jump around
|
||||
/// this slice based on indices, but don't necessarily use all of them.
|
||||
parents: &'m [(Option<ParentId>, &'m Ident)],
|
||||
|
||||
/// The unpacked field that is a leaf on the tree.
|
||||
leaf: &'m BorrowingFieldVisitorLeaf,
|
||||
}
|
||||
|
||||
impl SuccessType {
|
||||
/// Returns `true` if it's writeable, otherwise `false`.
|
||||
pub fn is_writeable(&self) -> bool {
|
||||
matches!(self, SuccessType::Writeable)
|
||||
}
|
||||
|
||||
/// Returns a return type, if it's not a writeable.
|
||||
pub fn as_type(&self) -> Option<&OutType> {
|
||||
match self {
|
||||
SuccessType::Writeable => None,
|
||||
SuccessType::OutType(ty) => Some(ty),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ReturnType {
|
||||
/// Returns `true` if it's writeable, otherwise `false`.
|
||||
pub fn is_writeable(&self) -> bool {
|
||||
self.return_type()
|
||||
.map(SuccessType::is_writeable)
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Returns the [`ReturnOk`] value, whether it's the single return type or
|
||||
/// the `Ok` variant of a result.
|
||||
pub fn return_type(&self) -> Option<&SuccessType> {
|
||||
match self {
|
||||
ReturnType::Infallible(ret) | ReturnType::Fallible(ret, _) => ret.as_ref(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the FFI function returns a value (such that it may be assigned to a variable).
|
||||
pub fn returns_value(&self) -> bool {
|
||||
match self {
|
||||
ReturnType::Fallible(_, _) => true,
|
||||
ReturnType::Infallible(Some(SuccessType::OutType(_))) => true,
|
||||
ReturnType::Infallible(Some(SuccessType::Writeable)) => false,
|
||||
ReturnType::Infallible(None) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ParamSelf {
|
||||
pub(super) fn new(ty: SelfType) -> Self {
|
||||
Self { ty }
|
||||
}
|
||||
|
||||
/// Return the number of fields and leaves that will show up in the [`BorrowingFieldVisitor`].
|
||||
///
|
||||
/// This method is used to calculate how much space to allocate upfront.
|
||||
fn field_leaf_lifetime_counts(&self, tcx: &TypeContext) -> (usize, usize) {
|
||||
match self.ty {
|
||||
SelfType::Opaque(_) => (1, 1),
|
||||
SelfType::Struct(ref ty) => ty.resolve(tcx).fields.iter().fold((1, 0), |acc, field| {
|
||||
let inner = field.ty.field_leaf_lifetime_counts(tcx);
|
||||
(acc.0 + inner.0, acc.1 + inner.1)
|
||||
}),
|
||||
SelfType::Enum(_) => (0, 0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Param {
|
||||
pub(super) fn new(name: IdentBuf, ty: Type) -> Self {
|
||||
Self { name, ty }
|
||||
}
|
||||
}
|
||||
|
||||
impl Method {
|
||||
/// Returns `true` if the method takes a writeable as an out parameter,
|
||||
/// otherwise `false`.
|
||||
pub fn is_writeable(&self) -> bool {
|
||||
self.output.is_writeable()
|
||||
}
|
||||
|
||||
/// Returns a fresh [`MethodLifetimes`] corresponding to `self`.
|
||||
pub fn method_lifetimes(&self) -> MethodLifetimes {
|
||||
self.lifetime_env.method_lifetimes()
|
||||
}
|
||||
|
||||
/// Returns a new [`BorrowingFieldVisitor`], which allocates memory to
|
||||
/// efficiently represent all fields (and their paths!) of the inputs that
|
||||
/// have a lifetime.
|
||||
/// ```ignore
|
||||
/// # use std::collections::BTreeMap;
|
||||
/// let visitor = method.borrowing_field_visitor(&tcx, "this".ck().unwrap());
|
||||
/// let mut map = BTreeMap::new();
|
||||
/// visitor.visit_borrowing_fields(|lifetime, field| {
|
||||
/// map.entry(lifetime).or_default().push(field);
|
||||
/// })
|
||||
/// ```
|
||||
pub fn borrowing_field_visitor<'m>(
|
||||
&'m self,
|
||||
tcx: &'m TypeContext,
|
||||
self_name: &'m Ident,
|
||||
) -> BorrowingFieldVisitor<'m> {
|
||||
BorrowingFieldVisitor::new(self, tcx, self_name)
|
||||
}
|
||||
}
|
||||
|
||||
impl ParentId {
|
||||
/// Pushes a new parent to the vec, returning the corresponding [`ParentId`].
|
||||
fn new<'m>(
|
||||
parent: Option<ParentId>,
|
||||
name: &'m Ident,
|
||||
parents: &mut SmallVec<[(Option<ParentId>, &'m Ident); 4]>,
|
||||
) -> Self {
|
||||
let this = ParentId(parents.len());
|
||||
parents.push((parent, name));
|
||||
this
|
||||
}
|
||||
}
|
||||
|
||||
impl<'m> BorrowingFieldVisitor<'m> {
|
||||
/// Visits every borrowing field and method lifetime that it uses.
|
||||
///
|
||||
/// The idea is that you could use this to construct a mapping from
|
||||
/// `MethodLifetime`s to `BorrowingField`s. We choose to use a visitor
|
||||
/// pattern to avoid having to
|
||||
///
|
||||
/// This would be convenient in the JavaScript backend where if you're
|
||||
/// returning an `NonOpaque<'a, 'b>` from Rust, you need to pass a
|
||||
/// `[[all input borrowing fields with 'a], [all input borrowing fields with 'b]]`
|
||||
/// array into `NonOpaque`'s constructor.
|
||||
///
|
||||
/// Alternatively, you could use such a map in the C++ backend by recursing
|
||||
/// down the return type and keeping track of which fields you've recursed
|
||||
/// into so far, and when you hit some lifetime 'a, generate docs saying
|
||||
/// "path.to.current.field must be outlived by {borrowing fields of input that
|
||||
/// contain 'a}".
|
||||
pub fn visit_borrowing_fields<'a, F>(&'a self, mut visit: F)
|
||||
where
|
||||
F: FnMut(MaybeStatic<MethodLifetime>, BorrowingField<'a>),
|
||||
{
|
||||
for leaf in self.leaves.iter() {
|
||||
let borrowing_field = BorrowingField {
|
||||
parents: self.parents.as_slice(),
|
||||
leaf,
|
||||
};
|
||||
|
||||
match leaf {
|
||||
BorrowingFieldVisitorLeaf::Opaque(_, lt, method_lifetimes) => {
|
||||
visit(*lt, borrowing_field);
|
||||
for lt in method_lifetimes.lifetimes() {
|
||||
visit(lt, borrowing_field);
|
||||
}
|
||||
}
|
||||
BorrowingFieldVisitorLeaf::Slice(_, lt) => {
|
||||
visit(*lt, borrowing_field);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a new `BorrowingFieldsVisitor` containing all the lifetime trees of the arguments
|
||||
/// in only two allocations.
|
||||
fn new(method: &'m Method, tcx: &'m TypeContext, self_name: &'m Ident) -> Self {
|
||||
let (parents, leaves) = method
|
||||
.param_self
|
||||
.as_ref()
|
||||
.map(|param_self| param_self.field_leaf_lifetime_counts(tcx))
|
||||
.into_iter()
|
||||
.chain(
|
||||
method
|
||||
.params
|
||||
.iter()
|
||||
.map(|param| param.ty.field_leaf_lifetime_counts(tcx)),
|
||||
)
|
||||
.reduce(|acc, x| (acc.0 + x.0, acc.1 + x.1))
|
||||
.map(|(num_fields, num_leaves)| {
|
||||
let num_params = method.params.len() + usize::from(method.param_self.is_some());
|
||||
let mut parents = SmallVec::with_capacity(num_fields + num_params);
|
||||
let mut leaves = SmallVec::with_capacity(num_leaves);
|
||||
let method_lifetimes = method.method_lifetimes();
|
||||
|
||||
if let Some(param_self) = method.param_self.as_ref() {
|
||||
let parent = ParentId::new(None, self_name, &mut parents);
|
||||
match ¶m_self.ty {
|
||||
SelfType::Opaque(ty) => {
|
||||
Self::visit_opaque(
|
||||
&ty.lifetimes,
|
||||
&ty.borrowed().lifetime,
|
||||
parent,
|
||||
&method_lifetimes,
|
||||
&mut leaves,
|
||||
);
|
||||
}
|
||||
SelfType::Struct(ty) => {
|
||||
Self::visit_struct(
|
||||
ty,
|
||||
tcx,
|
||||
parent,
|
||||
&method_lifetimes,
|
||||
&mut parents,
|
||||
&mut leaves,
|
||||
);
|
||||
}
|
||||
SelfType::Enum(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
for param in method.params.iter() {
|
||||
let parent = ParentId::new(None, param.name.as_ref(), &mut parents);
|
||||
Self::from_type(
|
||||
¶m.ty,
|
||||
tcx,
|
||||
parent,
|
||||
&method_lifetimes,
|
||||
&mut parents,
|
||||
&mut leaves,
|
||||
);
|
||||
}
|
||||
|
||||
// sanity check that the preallocations were correct
|
||||
debug_assert_eq!(
|
||||
parents.capacity(),
|
||||
std::cmp::max(INLINE_NUM_PARENTS, num_fields + num_params)
|
||||
);
|
||||
debug_assert_eq!(
|
||||
leaves.capacity(),
|
||||
std::cmp::max(INLINE_NUM_LEAVES, num_leaves)
|
||||
);
|
||||
(parents, leaves)
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
Self { parents, leaves }
|
||||
}
|
||||
|
||||
/// Returns a new [`BorrowingFieldsVisitor`] corresponding to a type.
|
||||
fn from_type(
|
||||
ty: &'m Type,
|
||||
tcx: &'m TypeContext,
|
||||
parent: ParentId,
|
||||
method_lifetimes: &MethodLifetimes,
|
||||
parents: &mut SmallVec<[(Option<ParentId>, &'m Ident); 4]>,
|
||||
leaves: &mut SmallVec<[BorrowingFieldVisitorLeaf; 8]>,
|
||||
) {
|
||||
match ty {
|
||||
Type::Opaque(path) => {
|
||||
Self::visit_opaque(
|
||||
&path.lifetimes,
|
||||
&path.borrowed().lifetime,
|
||||
parent,
|
||||
method_lifetimes,
|
||||
leaves,
|
||||
);
|
||||
}
|
||||
Type::Slice(slice) => {
|
||||
Self::visit_slice(slice, parent, method_lifetimes, leaves);
|
||||
}
|
||||
Type::Struct(path) => {
|
||||
Self::visit_struct(path, tcx, parent, method_lifetimes, parents, leaves);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
/// Add an opaque as a leaf during construction of a [`BorrowingFieldsVisitor`].
|
||||
fn visit_opaque(
|
||||
lifetimes: &'m TypeLifetimes,
|
||||
borrow: &'m MaybeStatic<TypeLifetime>,
|
||||
parent: ParentId,
|
||||
method_lifetimes: &MethodLifetimes,
|
||||
leaves: &mut SmallVec<[BorrowingFieldVisitorLeaf; 8]>,
|
||||
) {
|
||||
let method_borrow_lifetime =
|
||||
borrow.flat_map_nonstatic(|lt| lt.as_method_lifetime(method_lifetimes));
|
||||
let method_type_lifetimes = lifetimes.as_method_lifetimes(method_lifetimes);
|
||||
leaves.push(BorrowingFieldVisitorLeaf::Opaque(
|
||||
parent,
|
||||
method_borrow_lifetime,
|
||||
method_type_lifetimes,
|
||||
));
|
||||
}
|
||||
|
||||
/// Add a slice as a leaf during construction of a [`BorrowingFieldsVisitor`].
|
||||
fn visit_slice(
|
||||
slice: &Slice,
|
||||
parent: ParentId,
|
||||
method_lifetimes: &MethodLifetimes,
|
||||
leaves: &mut SmallVec<[BorrowingFieldVisitorLeaf; 8]>,
|
||||
) {
|
||||
let method_lifetime = slice
|
||||
.lifetime()
|
||||
.flat_map_nonstatic(|lt| lt.as_method_lifetime(method_lifetimes));
|
||||
leaves.push(BorrowingFieldVisitorLeaf::Slice(parent, method_lifetime));
|
||||
}
|
||||
|
||||
/// Add a struct as a parent and recurse down leaves during construction of a
|
||||
/// [`BorrowingFieldsVisitor`].
|
||||
fn visit_struct(
|
||||
ty: &paths::StructPath,
|
||||
tcx: &'m TypeContext,
|
||||
parent: ParentId,
|
||||
method_lifetimes: &MethodLifetimes,
|
||||
parents: &mut SmallVec<[(Option<ParentId>, &'m Ident); 4]>,
|
||||
leaves: &mut SmallVec<[BorrowingFieldVisitorLeaf; 8]>,
|
||||
) {
|
||||
let method_type_lifetimes = ty.lifetimes.as_method_lifetimes(method_lifetimes);
|
||||
for field in ty.resolve(tcx).fields.iter() {
|
||||
Self::from_type(
|
||||
&field.ty,
|
||||
tcx,
|
||||
ParentId::new(Some(parent), field.name.as_ref(), parents),
|
||||
&method_type_lifetimes,
|
||||
parents,
|
||||
leaves,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'m> BorrowingField<'m> {
|
||||
/// Visit fields in order.
|
||||
///
|
||||
/// If `self` represents the field `param.first.second`, then calling [`BorrowingField::trace`]
|
||||
/// will visit the following in order: `"param"`, `"first"`, `"second"`.
|
||||
pub fn backtrace<F>(&self, mut visit: F)
|
||||
where
|
||||
F: FnMut(usize, &'m Ident),
|
||||
{
|
||||
let (parent, ident) = match self.leaf {
|
||||
BorrowingFieldVisitorLeaf::Opaque(id, ..) | BorrowingFieldVisitorLeaf::Slice(id, _) => {
|
||||
self.parents[id.0]
|
||||
}
|
||||
};
|
||||
|
||||
self.backtrace_rec(parent, ident, &mut visit);
|
||||
}
|
||||
|
||||
/// Recursively visits fields in order from root to leaf by building up the
|
||||
/// stack, and then visiting fields as it unwinds.
|
||||
fn backtrace_rec<F>(&self, parent: Option<ParentId>, ident: &'m Ident, visit: &mut F) -> usize
|
||||
where
|
||||
F: FnMut(usize, &'m Ident),
|
||||
{
|
||||
let from_end = if let Some(id) = parent {
|
||||
let (parent, ident) = self.parents[id.0];
|
||||
self.backtrace_rec(parent, ident, visit)
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
visit(from_end, ident);
|
||||
|
||||
from_end + 1
|
||||
}
|
||||
|
||||
/// Fallibly visits fields in order.
|
||||
///
|
||||
/// This method is similar to [`BorrowinfField::backtrace`], but short-circuits
|
||||
/// when an `Err` is returned.
|
||||
pub fn try_backtrace<F, E>(&self, mut visit: F) -> Result<(), E>
|
||||
where
|
||||
F: FnMut(usize, &'m Ident) -> Result<(), E>,
|
||||
{
|
||||
let (parent, ident) = match self.leaf {
|
||||
BorrowingFieldVisitorLeaf::Opaque(id, ..) | BorrowingFieldVisitorLeaf::Slice(id, _) => {
|
||||
self.parents[id.0]
|
||||
}
|
||||
};
|
||||
|
||||
self.try_backtrace_rec(parent, ident, &mut visit)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Recursively visits fields in order from root to leaf by building up the
|
||||
/// stack, and then visiting fields as it unwinds.
|
||||
fn try_backtrace_rec<F, E>(
|
||||
&self,
|
||||
parent: Option<ParentId>,
|
||||
ident: &'m Ident,
|
||||
visit: &mut F,
|
||||
) -> Result<usize, E>
|
||||
where
|
||||
F: FnMut(usize, &'m Ident) -> Result<(), E>,
|
||||
{
|
||||
let from_end = if let Some(id) = parent {
|
||||
let (parent, ident) = self.parents[id.0];
|
||||
self.try_backtrace_rec(parent, ident, visit)?
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
visit(from_end, ident)?;
|
||||
|
||||
Ok(from_end + 1)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'m> fmt::Display for BorrowingField<'m> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.try_backtrace(|i, ident| {
|
||||
if i != 0 {
|
||||
f.write_char('.')?;
|
||||
}
|
||||
f.write_str(ident.as_str())
|
||||
})
|
||||
}
|
||||
}
|
29
third_party/rust/diplomat_core/src/hir/mod.rs
vendored
Normal file
29
third_party/rust/diplomat_core/src/hir/mod.rs
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
//! Experimental high-level representation (HIR) for Diplomat.
|
||||
//!
|
||||
//! Enabled with the `"hir"` Cargo feature
|
||||
|
||||
mod defs;
|
||||
mod elision;
|
||||
mod lifetimes;
|
||||
mod lowering;
|
||||
mod methods;
|
||||
mod paths;
|
||||
mod primitives;
|
||||
mod ty_position;
|
||||
mod type_context;
|
||||
mod types;
|
||||
pub use defs::*;
|
||||
pub(super) use elision::*;
|
||||
pub use lifetimes::*;
|
||||
pub(super) use lowering::*;
|
||||
pub use methods::*;
|
||||
pub use paths::*;
|
||||
pub use primitives::*;
|
||||
pub use ty_position::*;
|
||||
pub use type_context::*;
|
||||
pub use types::*;
|
||||
|
||||
pub use lowering::LoweringError;
|
||||
|
||||
pub use crate::ast::Docs;
|
||||
pub use strck_ident::rust::{Ident, IdentBuf};
|
167
third_party/rust/diplomat_core/src/hir/paths.rs
vendored
Normal file
167
third_party/rust/diplomat_core/src/hir/paths.rs
vendored
Normal file
@ -0,0 +1,167 @@
|
||||
use super::{
|
||||
Borrow, EnumDef, EnumId, Everywhere, OpaqueDef, OpaqueId, OpaqueOwner, OutStructDef,
|
||||
OutputOnly, ReturnableStructDef, StructDef, TyPosition, TypeContext, TypeLifetimes,
|
||||
};
|
||||
|
||||
/// Path to a struct that may appear as an output.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ReturnableStructPath {
|
||||
Struct(StructPath),
|
||||
OutStruct(OutStructPath),
|
||||
}
|
||||
|
||||
/// Path to a struct that can only be used as an output.
|
||||
pub type OutStructPath = StructPath<OutputOnly>;
|
||||
|
||||
/// Path to a struct that can be used in inputs and outputs.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct StructPath<P: TyPosition = Everywhere> {
|
||||
pub lifetimes: TypeLifetimes,
|
||||
pub tcx_id: P::StructId,
|
||||
}
|
||||
|
||||
/// Path to an opaque.
|
||||
///
|
||||
/// There are three kinds of opaques that Diplomat uses, so this type has two
|
||||
/// generic arguments to differentiate between the three, while still showing
|
||||
/// that the three are all paths to opaques. The monomorphized versions that
|
||||
/// Diplomat uses are:
|
||||
///
|
||||
/// 1. `OpaquePath<Optional, MaybeOwn>`: Opaques in return types,
|
||||
/// which can be optional and either owned or borrowed.
|
||||
/// 2. `OpaquePath<Optional, Borrow>`: Opaques in method parameters, which can
|
||||
/// be optional but must be borrowed, since most languages don't have a way to
|
||||
/// entirely give up ownership of a value.
|
||||
/// 3. `OpaquePath<NonOptional, Borrow>`: Opaques in the `&self` position, which
|
||||
/// cannot be optional and must be borrowed for the same reason as above.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct OpaquePath<Opt, Owner> {
|
||||
pub lifetimes: TypeLifetimes,
|
||||
pub optional: Opt,
|
||||
pub owner: Owner,
|
||||
pub tcx_id: OpaqueId,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct Optional(pub(super) bool);
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct NonOptional;
|
||||
|
||||
impl<Owner: OpaqueOwner> OpaquePath<Optional, Owner> {
|
||||
pub fn is_optional(&self) -> bool {
|
||||
self.optional.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<Owner: OpaqueOwner> OpaquePath<NonOptional, Owner> {
|
||||
pub fn wrap_optional(self) -> OpaquePath<Optional, Owner> {
|
||||
OpaquePath {
|
||||
lifetimes: self.lifetimes,
|
||||
optional: Optional(false),
|
||||
owner: self.owner,
|
||||
tcx_id: self.tcx_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Opt> OpaquePath<Opt, MaybeOwn> {
|
||||
pub fn as_borrowed(&self) -> Option<&Borrow> {
|
||||
self.owner.as_borrowed()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Opt> OpaquePath<Opt, Borrow> {
|
||||
pub fn borrowed(&self) -> &Borrow {
|
||||
&self.owner
|
||||
}
|
||||
}
|
||||
|
||||
/// Path to an enum.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EnumPath {
|
||||
pub tcx_id: EnumId,
|
||||
}
|
||||
|
||||
/// Determine whether a pointer to an opaque type is owned or borrowed.
|
||||
///
|
||||
/// Since owned opaques cannot be used as inputs, this only appears in output types.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum MaybeOwn {
|
||||
Own,
|
||||
Borrow(Borrow),
|
||||
}
|
||||
|
||||
impl MaybeOwn {
|
||||
pub fn as_borrowed(&self) -> Option<&Borrow> {
|
||||
match self {
|
||||
MaybeOwn::Own => None,
|
||||
MaybeOwn::Borrow(borrow) => Some(borrow),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ReturnableStructPath {
|
||||
pub fn resolve<'tcx>(&self, tcx: &'tcx TypeContext) -> ReturnableStructDef<'tcx> {
|
||||
match self {
|
||||
ReturnableStructPath::Struct(path) => ReturnableStructDef::Struct(path.resolve(tcx)),
|
||||
ReturnableStructPath::OutStruct(path) => {
|
||||
ReturnableStructDef::OutStruct(path.resolve(tcx))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: TyPosition> StructPath<P> {
|
||||
/// Returns a new [`EnumPath`].
|
||||
pub(super) fn new(lifetimes: TypeLifetimes, tcx_id: P::StructId) -> Self {
|
||||
Self { lifetimes, tcx_id }
|
||||
}
|
||||
}
|
||||
impl StructPath {
|
||||
/// Returns the [`StructDef`] that this path references.
|
||||
pub fn resolve<'tcx>(&self, tcx: &'tcx TypeContext) -> &'tcx StructDef {
|
||||
tcx.resolve_struct(self.tcx_id)
|
||||
}
|
||||
}
|
||||
|
||||
impl OutStructPath {
|
||||
/// Returns the [`OutStructDef`] that this path references.
|
||||
pub fn resolve<'tcx>(&self, tcx: &'tcx TypeContext) -> &'tcx OutStructDef {
|
||||
tcx.resolve_out_struct(self.tcx_id)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Opt, Owner> OpaquePath<Opt, Owner> {
|
||||
/// Returns a new [`EnumPath`].
|
||||
pub(super) fn new(
|
||||
lifetimes: TypeLifetimes,
|
||||
optional: Opt,
|
||||
owner: Owner,
|
||||
tcx_id: OpaqueId,
|
||||
) -> Self {
|
||||
Self {
|
||||
lifetimes,
|
||||
optional,
|
||||
owner,
|
||||
tcx_id,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the [`OpaqueDef`] that this path references.
|
||||
pub fn resolve<'tcx>(&self, tcx: &'tcx TypeContext) -> &'tcx OpaqueDef {
|
||||
tcx.resolve_opaque(self.tcx_id)
|
||||
}
|
||||
}
|
||||
|
||||
impl EnumPath {
|
||||
/// Returns a new [`EnumPath`].
|
||||
pub(super) fn new(tcx_id: EnumId) -> Self {
|
||||
Self { tcx_id }
|
||||
}
|
||||
|
||||
/// Returns the [`EnumDef`] that this path references.
|
||||
pub fn resolve<'tcx>(&self, tcx: &'tcx TypeContext) -> &'tcx EnumDef {
|
||||
tcx.resolve_enum(self.tcx_id)
|
||||
}
|
||||
}
|
128
third_party/rust/diplomat_core/src/hir/primitives.rs
vendored
Normal file
128
third_party/rust/diplomat_core/src/hir/primitives.rs
vendored
Normal file
@ -0,0 +1,128 @@
|
||||
//! Primitives types that can cross the FFI boundary.
|
||||
use crate::ast;
|
||||
|
||||
/// 8, 16, 32, and 64-bit signed and unsigned integers.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum IntType {
|
||||
I8,
|
||||
I16,
|
||||
I32,
|
||||
I64,
|
||||
U8,
|
||||
U16,
|
||||
U32,
|
||||
U64,
|
||||
}
|
||||
|
||||
/// Platform-dependent signed and unsigned size types.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum IntSizeType {
|
||||
Isize,
|
||||
Usize,
|
||||
}
|
||||
|
||||
/// 128-bit signed and unsigned integers.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum Int128Type {
|
||||
I128,
|
||||
U128,
|
||||
}
|
||||
|
||||
/// 32 and 64-bit floating point numbers.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum FloatType {
|
||||
F32,
|
||||
F64,
|
||||
}
|
||||
|
||||
/// All primitive types.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum PrimitiveType {
|
||||
Bool,
|
||||
Char,
|
||||
Int(IntType),
|
||||
IntSize(IntSizeType),
|
||||
Int128(Int128Type),
|
||||
Float(FloatType),
|
||||
}
|
||||
|
||||
impl IntType {
|
||||
/// Returns the string representation of `self`.
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
IntType::I8 => "i8",
|
||||
IntType::I16 => "i16",
|
||||
IntType::I32 => "i32",
|
||||
IntType::I64 => "i64",
|
||||
IntType::U8 => "u8",
|
||||
IntType::U16 => "u16",
|
||||
IntType::U32 => "u32",
|
||||
IntType::U64 => "u64",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntSizeType {
|
||||
/// Returns the string representation of `self`.
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
IntSizeType::Isize => "isize",
|
||||
IntSizeType::Usize => "usize",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Int128Type {
|
||||
/// Returns the string representation of `self`.
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
Int128Type::I128 => "i128",
|
||||
Int128Type::U128 => "u128",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FloatType {
|
||||
/// Returns the string representation of `self`.
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
FloatType::F32 => "f32",
|
||||
FloatType::F64 => "f64",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PrimitiveType {
|
||||
pub(super) fn from_ast(prim: ast::PrimitiveType) -> Self {
|
||||
match prim {
|
||||
ast::PrimitiveType::i8 => PrimitiveType::Int(IntType::I8),
|
||||
ast::PrimitiveType::u8 => PrimitiveType::Int(IntType::U8),
|
||||
ast::PrimitiveType::i16 => PrimitiveType::Int(IntType::I16),
|
||||
ast::PrimitiveType::u16 => PrimitiveType::Int(IntType::U16),
|
||||
ast::PrimitiveType::i32 => PrimitiveType::Int(IntType::I32),
|
||||
ast::PrimitiveType::u32 => PrimitiveType::Int(IntType::U32),
|
||||
ast::PrimitiveType::i64 => PrimitiveType::Int(IntType::I64),
|
||||
ast::PrimitiveType::u64 => PrimitiveType::Int(IntType::U64),
|
||||
ast::PrimitiveType::isize => PrimitiveType::IntSize(IntSizeType::Isize),
|
||||
ast::PrimitiveType::usize => PrimitiveType::IntSize(IntSizeType::Usize),
|
||||
ast::PrimitiveType::i128 => PrimitiveType::Int128(Int128Type::I128),
|
||||
ast::PrimitiveType::u128 => PrimitiveType::Int128(Int128Type::U128),
|
||||
ast::PrimitiveType::f32 => PrimitiveType::Float(FloatType::F32),
|
||||
ast::PrimitiveType::f64 => PrimitiveType::Float(FloatType::F64),
|
||||
ast::PrimitiveType::bool => PrimitiveType::Bool,
|
||||
ast::PrimitiveType::char => PrimitiveType::Char,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the string representation of `self`.
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
PrimitiveType::Bool => "bool",
|
||||
PrimitiveType::Char => "char",
|
||||
PrimitiveType::Int(ty) => ty.as_str(),
|
||||
PrimitiveType::IntSize(ty) => ty.as_str(),
|
||||
PrimitiveType::Int128(ty) => ty.as_str(),
|
||||
PrimitiveType::Float(ty) => ty.as_str(),
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
---
|
||||
source: core/src/hir/elision.rs
|
||||
expression: map
|
||||
---
|
||||
{
|
||||
Static: [
|
||||
"this.name",
|
||||
"_s",
|
||||
],
|
||||
NonStatic(
|
||||
MethodLifetime(
|
||||
0,
|
||||
),
|
||||
): [
|
||||
"this.p_data",
|
||||
],
|
||||
NonStatic(
|
||||
MethodLifetime(
|
||||
1,
|
||||
),
|
||||
): [
|
||||
"this.q_data",
|
||||
"this.inner.more_data",
|
||||
],
|
||||
}
|
208
third_party/rust/diplomat_core/src/hir/snapshots/diplomat_core__hir__elision__tests__simple_mod.snap
vendored
Normal file
208
third_party/rust/diplomat_core/src/hir/snapshots/diplomat_core__hir__elision__tests__simple_mod.snap
vendored
Normal file
@ -0,0 +1,208 @@
|
||||
---
|
||||
source: core/src/hir/elision.rs
|
||||
expression: tcx
|
||||
---
|
||||
TypeContext {
|
||||
out_structs: [
|
||||
StructDef {
|
||||
docs: Docs(
|
||||
"",
|
||||
[],
|
||||
),
|
||||
name: "OutStruct",
|
||||
fields: [
|
||||
StructField {
|
||||
docs: Docs(
|
||||
"",
|
||||
[],
|
||||
),
|
||||
name: "inner",
|
||||
ty: Opaque(
|
||||
OpaquePath {
|
||||
lifetimes: TypeLifetimes {
|
||||
indices: [
|
||||
NonStatic(
|
||||
TypeLifetime(
|
||||
0,
|
||||
),
|
||||
),
|
||||
],
|
||||
},
|
||||
optional: Optional(
|
||||
true,
|
||||
),
|
||||
owner: Own,
|
||||
tcx_id: OpaqueId(
|
||||
0,
|
||||
),
|
||||
},
|
||||
),
|
||||
},
|
||||
],
|
||||
methods: [
|
||||
Method {
|
||||
docs: Docs(
|
||||
"",
|
||||
[],
|
||||
),
|
||||
name: "new",
|
||||
lifetime_env: LifetimeEnv {
|
||||
nodes: [
|
||||
Lifetime {
|
||||
ident: "a",
|
||||
longer: [],
|
||||
shorter: [],
|
||||
},
|
||||
],
|
||||
num_lifetimes: 1,
|
||||
},
|
||||
param_self: None,
|
||||
params: [
|
||||
Param {
|
||||
name: "s",
|
||||
ty: Slice(
|
||||
Str(
|
||||
NonStatic(
|
||||
TypeLifetime(
|
||||
0,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
],
|
||||
output: Infallible(
|
||||
Some(
|
||||
OutType(
|
||||
Struct(
|
||||
OutStruct(
|
||||
StructPath {
|
||||
lifetimes: TypeLifetimes {
|
||||
indices: [
|
||||
NonStatic(
|
||||
TypeLifetime(
|
||||
0,
|
||||
),
|
||||
),
|
||||
],
|
||||
},
|
||||
tcx_id: OutStructId(
|
||||
0,
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
structs: [
|
||||
StructDef {
|
||||
docs: Docs(
|
||||
"",
|
||||
[],
|
||||
),
|
||||
name: "Struct",
|
||||
fields: [
|
||||
StructField {
|
||||
docs: Docs(
|
||||
"",
|
||||
[],
|
||||
),
|
||||
name: "s",
|
||||
ty: Slice(
|
||||
Str(
|
||||
NonStatic(
|
||||
TypeLifetime(
|
||||
0,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
],
|
||||
methods: [
|
||||
Method {
|
||||
docs: Docs(
|
||||
"",
|
||||
[],
|
||||
),
|
||||
name: "rustc_elision",
|
||||
lifetime_env: LifetimeEnv {
|
||||
nodes: [
|
||||
Lifetime {
|
||||
ident: "a",
|
||||
longer: [],
|
||||
shorter: [],
|
||||
},
|
||||
],
|
||||
num_lifetimes: 2,
|
||||
},
|
||||
param_self: Some(
|
||||
ParamSelf {
|
||||
ty: Struct(
|
||||
StructPath {
|
||||
lifetimes: TypeLifetimes {
|
||||
indices: [
|
||||
NonStatic(
|
||||
TypeLifetime(
|
||||
0,
|
||||
),
|
||||
),
|
||||
],
|
||||
},
|
||||
tcx_id: StructId(
|
||||
0,
|
||||
),
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
params: [
|
||||
Param {
|
||||
name: "s",
|
||||
ty: Slice(
|
||||
Str(
|
||||
NonStatic(
|
||||
TypeLifetime(
|
||||
1,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
],
|
||||
output: Infallible(
|
||||
Some(
|
||||
OutType(
|
||||
Slice(
|
||||
Str(
|
||||
NonStatic(
|
||||
TypeLifetime(
|
||||
1,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
opaques: [
|
||||
OpaqueDef {
|
||||
docs: Docs(
|
||||
"",
|
||||
[],
|
||||
),
|
||||
name: "Opaque",
|
||||
methods: [],
|
||||
},
|
||||
],
|
||||
enums: [],
|
||||
}
|
182
third_party/rust/diplomat_core/src/hir/ty_position.rs
vendored
Normal file
182
third_party/rust/diplomat_core/src/hir/ty_position.rs
vendored
Normal file
@ -0,0 +1,182 @@
|
||||
use super::{
|
||||
Borrow, MaybeOwn, Mutability, OutStructId, ReturnableStructPath, StructId, StructPath, TypeId,
|
||||
};
|
||||
use core::fmt::Debug;
|
||||
|
||||
/// Abstraction over where a type can appear in a function signature.
|
||||
///
|
||||
/// # "Output only" and "everywhere" types
|
||||
///
|
||||
/// While Rust is able to give up ownership of values, languages that Diplomat
|
||||
/// supports (C++, Javascript, etc.) generally cannot. For example, we can
|
||||
/// construct a `Box<MyOpaque>` in a Rust function and _return_ it to the other
|
||||
/// language as a pointer. However, we cannot _accept_ `Box<MyOpaque>` as an input
|
||||
/// because there's nothing stopping other languages from using that value again.
|
||||
/// Therefore, we classify boxed opaques as "output only" types, since they can
|
||||
/// only be returned from Rust but not taken as inputs.
|
||||
///
|
||||
/// Furthermore, Diplomat also supports "bag o' stuff" structs where all fields get
|
||||
/// translated at the boundary. If one contains an "output only" type as a field,
|
||||
/// then the whole struct must also be "output only". In particular, this means
|
||||
/// that if a boxed opaque is nested in a bunch of "bag o' stuff" structs, than
|
||||
/// all of those structs must also be "output only".
|
||||
///
|
||||
/// Currently, there are only two classes of structs: those that are "output only",
|
||||
/// and those that are not. These are represented by the types [`OutputOnly`]
|
||||
/// and [`Everywhere`] marker types respectively, which are the _only_ two types
|
||||
/// that implement [`TyPosition`].
|
||||
///
|
||||
/// # How does abstraction help?
|
||||
///
|
||||
/// The HIR was designed around the idea of making invalid states unrepresentable.
|
||||
/// Since "output only" types can contain values that "everywhere" types cannot,
|
||||
/// it doesn't make sense for them to be represented in the same type, even if
|
||||
/// they're mostly the same. One of these differences is that opaques (which are
|
||||
/// always behind a pointer) can only be represented as a borrow in "everywhere"
|
||||
/// types, but can additionally be represented as owned in "output only" types.
|
||||
/// If we were to use the same type for both, then backends working with "everywhere"
|
||||
/// types would constantly have unreachable statements for owned opaque cases.
|
||||
///
|
||||
/// That being said, "output only" and "everywhere" types are still mostly the
|
||||
/// same, so this trait allows us to describe the differences. For example, the
|
||||
/// HIR uses a singular [`Type`](super::Type) type for representing both
|
||||
/// "output only" types and "everywhere" types, since it takes advantage of this
|
||||
/// traits associated types to "fill in" the different parts:
|
||||
/// ```ignore
|
||||
/// pub enum Type<P: TyPosition = Everywhere> {
|
||||
/// Primitive(PrimitiveType),
|
||||
/// Opaque(OpaquePath<Optional, P::OpaqueOwnership>),
|
||||
/// Struct(P::StructPath),
|
||||
/// Enum(EnumPath),
|
||||
/// Slice(Slice),
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// When `P` takes on [`Everywhere`], this signature becomes:
|
||||
/// ```ignore
|
||||
/// pub enum Type {
|
||||
/// Primitive(PrimitiveType),
|
||||
/// Opaque(OpaquePath<Optional, Borrow>),
|
||||
/// Struct(StructPath),
|
||||
/// Enum(EnumPath),
|
||||
/// Slice(Slice),
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// This allows us to represent any kind of type that can appear "everywhere"
|
||||
/// i.e. in inputs or outputs. Notice how the second generic in the `Opaque`
|
||||
/// variant becomes [`Borrow`]. This describes the semantics of the pointer that
|
||||
/// the opaque lives behind, and shows that for "everywhere" types, opaques
|
||||
/// can _only_ be represented as living behind a borrow.
|
||||
///
|
||||
/// Contrast this to when `P` takes on [`OutputOnly`]:
|
||||
/// ```ignore
|
||||
/// pub enum Type {
|
||||
/// Primitive(PrimitiveType),
|
||||
/// Opaque(OpaquePath<Optional, MaybeOwn>),
|
||||
/// Struct(OutStructPath),
|
||||
/// Enum(EnumPath),
|
||||
/// Slice(Slice),
|
||||
/// }
|
||||
/// ```
|
||||
/// Here, the second generic of the `Opaque` variant becomes [`MaybeOwn`], meaning
|
||||
/// that "output only" types can contain opaques that are either borrowed _or_ owned.
|
||||
///
|
||||
/// Therefore, this trait allows be extremely precise about making invalid states
|
||||
/// unrepresentable, while also reducing duplicated code.
|
||||
///
|
||||
pub trait TyPosition: Debug + Copy {
|
||||
const IS_OUT_ONLY: bool;
|
||||
|
||||
/// Type representing how we can point to opaques, which must always be behind a pointer.
|
||||
///
|
||||
/// The types represented by [`OutputOnly`] are capable of either owning or
|
||||
/// borrowing opaques, and so the associated type for that impl is [`MaybeOwn`].
|
||||
///
|
||||
/// On the other hand, types represented by [`Everywhere`] can only contain
|
||||
/// borrowes, so the associated type for that impl is [`Borrow`].
|
||||
type OpaqueOwnership: Debug + OpaqueOwner;
|
||||
|
||||
type StructId: Debug;
|
||||
|
||||
type StructPath: Debug;
|
||||
|
||||
fn id_for_path(p: &Self::StructPath) -> TypeId;
|
||||
}
|
||||
|
||||
/// One of two types implementing [`TyPosition`], representing types that can be
|
||||
/// used as both input and output to functions.
|
||||
///
|
||||
/// The complement of this type is [`OutputOnly`].
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct Everywhere;
|
||||
|
||||
/// One of two types implementing [`TyPosition`], representing types that can
|
||||
/// only be used as return types in functions.
|
||||
///
|
||||
/// The complement of this type is [`Everywhere`].
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct OutputOnly;
|
||||
|
||||
impl TyPosition for Everywhere {
|
||||
const IS_OUT_ONLY: bool = false;
|
||||
type OpaqueOwnership = Borrow;
|
||||
type StructId = StructId;
|
||||
type StructPath = StructPath;
|
||||
|
||||
fn id_for_path(p: &Self::StructPath) -> TypeId {
|
||||
p.tcx_id.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl TyPosition for OutputOnly {
|
||||
const IS_OUT_ONLY: bool = true;
|
||||
type OpaqueOwnership = MaybeOwn;
|
||||
type StructId = OutStructId;
|
||||
type StructPath = ReturnableStructPath;
|
||||
fn id_for_path(p: &Self::StructPath) -> TypeId {
|
||||
match p {
|
||||
ReturnableStructPath::Struct(p) => p.tcx_id.into(),
|
||||
ReturnableStructPath::OutStruct(p) => p.tcx_id.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Abstraction over how a type can hold a pointer to an opaque.
|
||||
///
|
||||
/// This trait is designed as a helper abstraction for the `OpaqueOwnership`
|
||||
/// associated type in the [`TyPosition`] trait. As such, only has two implementing
|
||||
/// types: [`MaybeOwn`] and [`Borrow`] for the [`OutputOnly`] and [`Everywhere`]
|
||||
/// implementations of [`TyPosition`] respectively.
|
||||
pub trait OpaqueOwner {
|
||||
/// Return the mutability of this owner
|
||||
fn mutability(&self) -> Option<Mutability>;
|
||||
|
||||
fn is_owned(&self) -> bool;
|
||||
}
|
||||
|
||||
impl OpaqueOwner for MaybeOwn {
|
||||
fn mutability(&self) -> Option<Mutability> {
|
||||
match self {
|
||||
MaybeOwn::Own => None,
|
||||
MaybeOwn::Borrow(b) => b.mutability(),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_owned(&self) -> bool {
|
||||
match self {
|
||||
MaybeOwn::Own => true,
|
||||
MaybeOwn::Borrow(_) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl OpaqueOwner for Borrow {
|
||||
fn mutability(&self) -> Option<Mutability> {
|
||||
Some(self.mutability)
|
||||
}
|
||||
|
||||
fn is_owned(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user