forked from libre-chip/fayalite
		
	initial public commit
This commit is contained in:
		
						commit
						0b958e7852
					
				
					 56 changed files with 30235 additions and 0 deletions
				
			
		
							
								
								
									
										2
									
								
								.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,2 @@
 | 
			
		|||
/target
 | 
			
		||||
.vscode
 | 
			
		||||
							
								
								
									
										593
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										593
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,593 @@
 | 
			
		|||
# This file is automatically @generated by Cargo.
 | 
			
		||||
# It is not intended for manual editing.
 | 
			
		||||
version = 3
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "ahash"
 | 
			
		||||
version = "0.8.7"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "cfg-if",
 | 
			
		||||
 "once_cell",
 | 
			
		||||
 "version_check",
 | 
			
		||||
 "zerocopy",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "allocator-api2"
 | 
			
		||||
version = "0.2.16"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "autocfg"
 | 
			
		||||
version = "1.1.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "base16ct"
 | 
			
		||||
version = "0.2.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "basic-toml"
 | 
			
		||||
version = "0.1.8"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "2db21524cad41c5591204d22d75e1970a2d1f71060214ca931dc7d5afe2c14e5"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "serde",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "bitflags"
 | 
			
		||||
version = "2.4.2"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "bitvec"
 | 
			
		||||
version = "1.0.1"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "funty",
 | 
			
		||||
 "radium",
 | 
			
		||||
 "serde",
 | 
			
		||||
 "tap",
 | 
			
		||||
 "wyz",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "block-buffer"
 | 
			
		||||
version = "0.10.4"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "generic-array",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "cfg-if"
 | 
			
		||||
version = "1.0.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "cpufeatures"
 | 
			
		||||
version = "0.2.12"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "libc",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "crypto-common"
 | 
			
		||||
version = "0.1.6"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "generic-array",
 | 
			
		||||
 "typenum",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "digest"
 | 
			
		||||
version = "0.10.7"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "block-buffer",
 | 
			
		||||
 "crypto-common",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "equivalent"
 | 
			
		||||
version = "1.0.1"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "errno"
 | 
			
		||||
version = "0.3.8"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "libc",
 | 
			
		||||
 "windows-sys",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "fastrand"
 | 
			
		||||
version = "2.0.1"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "fayalite"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "bitvec",
 | 
			
		||||
 "fayalite-proc-macros",
 | 
			
		||||
 "fayalite-visit-gen",
 | 
			
		||||
 "hashbrown",
 | 
			
		||||
 "num-bigint",
 | 
			
		||||
 "num-traits",
 | 
			
		||||
 "paste",
 | 
			
		||||
 "serde",
 | 
			
		||||
 "serde_json",
 | 
			
		||||
 "trybuild",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "fayalite-proc-macros"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "fayalite-proc-macros-impl",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "fayalite-proc-macros-impl"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "base16ct",
 | 
			
		||||
 "num-bigint",
 | 
			
		||||
 "prettyplease",
 | 
			
		||||
 "proc-macro2",
 | 
			
		||||
 "quote",
 | 
			
		||||
 "sha2",
 | 
			
		||||
 "syn",
 | 
			
		||||
 "tempfile",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "fayalite-visit-gen"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "indexmap",
 | 
			
		||||
 "prettyplease",
 | 
			
		||||
 "proc-macro2",
 | 
			
		||||
 "quote",
 | 
			
		||||
 "serde",
 | 
			
		||||
 "serde_json",
 | 
			
		||||
 "syn",
 | 
			
		||||
 "thiserror",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "funty"
 | 
			
		||||
version = "2.0.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "generic-array"
 | 
			
		||||
version = "0.14.7"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "typenum",
 | 
			
		||||
 "version_check",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "glob"
 | 
			
		||||
version = "0.3.1"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "hashbrown"
 | 
			
		||||
version = "0.14.3"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "ahash",
 | 
			
		||||
 "allocator-api2",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "indexmap"
 | 
			
		||||
version = "2.2.6"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "equivalent",
 | 
			
		||||
 "hashbrown",
 | 
			
		||||
 "serde",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "itoa"
 | 
			
		||||
version = "1.0.10"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "libc"
 | 
			
		||||
version = "0.2.153"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "linux-raw-sys"
 | 
			
		||||
version = "0.4.13"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "num-bigint"
 | 
			
		||||
version = "0.4.4"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "autocfg",
 | 
			
		||||
 "num-integer",
 | 
			
		||||
 "num-traits",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "num-integer"
 | 
			
		||||
version = "0.1.46"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "num-traits",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "num-traits"
 | 
			
		||||
version = "0.2.18"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "autocfg",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "once_cell"
 | 
			
		||||
version = "1.19.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "paste"
 | 
			
		||||
version = "1.0.14"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "prettyplease"
 | 
			
		||||
version = "0.2.20"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "proc-macro2",
 | 
			
		||||
 "syn",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "proc-macro2"
 | 
			
		||||
version = "1.0.83"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "unicode-ident",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "quote"
 | 
			
		||||
version = "1.0.36"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "proc-macro2",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "radium"
 | 
			
		||||
version = "0.7.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "rustix"
 | 
			
		||||
version = "0.38.31"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "bitflags",
 | 
			
		||||
 "errno",
 | 
			
		||||
 "libc",
 | 
			
		||||
 "linux-raw-sys",
 | 
			
		||||
 "windows-sys",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "ryu"
 | 
			
		||||
version = "1.0.17"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "serde"
 | 
			
		||||
version = "1.0.202"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "serde_derive",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "serde_derive"
 | 
			
		||||
version = "1.0.202"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "proc-macro2",
 | 
			
		||||
 "quote",
 | 
			
		||||
 "syn",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "serde_json"
 | 
			
		||||
version = "1.0.117"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "indexmap",
 | 
			
		||||
 "itoa",
 | 
			
		||||
 "ryu",
 | 
			
		||||
 "serde",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "sha2"
 | 
			
		||||
version = "0.10.8"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "cfg-if",
 | 
			
		||||
 "cpufeatures",
 | 
			
		||||
 "digest",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "syn"
 | 
			
		||||
version = "2.0.66"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "proc-macro2",
 | 
			
		||||
 "quote",
 | 
			
		||||
 "unicode-ident",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "tap"
 | 
			
		||||
version = "1.0.1"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "tempfile"
 | 
			
		||||
version = "3.10.1"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "cfg-if",
 | 
			
		||||
 "fastrand",
 | 
			
		||||
 "rustix",
 | 
			
		||||
 "windows-sys",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "termcolor"
 | 
			
		||||
version = "1.4.1"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "winapi-util",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "thiserror"
 | 
			
		||||
version = "1.0.61"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "thiserror-impl",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "thiserror-impl"
 | 
			
		||||
version = "1.0.61"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "proc-macro2",
 | 
			
		||||
 "quote",
 | 
			
		||||
 "syn",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "trybuild"
 | 
			
		||||
version = "1.0.89"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "9a9d3ba662913483d6722303f619e75ea10b7855b0f8e0d72799cf8621bb488f"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "basic-toml",
 | 
			
		||||
 "glob",
 | 
			
		||||
 "once_cell",
 | 
			
		||||
 "serde",
 | 
			
		||||
 "serde_derive",
 | 
			
		||||
 "serde_json",
 | 
			
		||||
 "termcolor",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "typenum"
 | 
			
		||||
version = "1.17.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "unicode-ident"
 | 
			
		||||
version = "1.0.12"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "version_check"
 | 
			
		||||
version = "0.9.4"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "winapi"
 | 
			
		||||
version = "0.3.9"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "winapi-i686-pc-windows-gnu",
 | 
			
		||||
 "winapi-x86_64-pc-windows-gnu",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "winapi-i686-pc-windows-gnu"
 | 
			
		||||
version = "0.4.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "winapi-util"
 | 
			
		||||
version = "0.1.6"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "winapi",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "winapi-x86_64-pc-windows-gnu"
 | 
			
		||||
version = "0.4.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "windows-sys"
 | 
			
		||||
version = "0.52.0"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "windows-targets",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "windows-targets"
 | 
			
		||||
version = "0.52.4"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "windows_aarch64_gnullvm",
 | 
			
		||||
 "windows_aarch64_msvc",
 | 
			
		||||
 "windows_i686_gnu",
 | 
			
		||||
 "windows_i686_msvc",
 | 
			
		||||
 "windows_x86_64_gnu",
 | 
			
		||||
 "windows_x86_64_gnullvm",
 | 
			
		||||
 "windows_x86_64_msvc",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "windows_aarch64_gnullvm"
 | 
			
		||||
version = "0.52.4"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "windows_aarch64_msvc"
 | 
			
		||||
version = "0.52.4"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "windows_i686_gnu"
 | 
			
		||||
version = "0.52.4"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "windows_i686_msvc"
 | 
			
		||||
version = "0.52.4"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "windows_x86_64_gnu"
 | 
			
		||||
version = "0.52.4"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "windows_x86_64_gnullvm"
 | 
			
		||||
version = "0.52.4"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "windows_x86_64_msvc"
 | 
			
		||||
version = "0.52.4"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "wyz"
 | 
			
		||||
version = "0.5.1"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "tap",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "zerocopy"
 | 
			
		||||
version = "0.7.32"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "zerocopy-derive",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "zerocopy-derive"
 | 
			
		||||
version = "0.7.32"
 | 
			
		||||
source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "proc-macro2",
 | 
			
		||||
 "quote",
 | 
			
		||||
 "syn",
 | 
			
		||||
]
 | 
			
		||||
							
								
								
									
										5
									
								
								Cargo.toml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								Cargo.toml
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,5 @@
 | 
			
		|||
# SPDX-License-Identifier: LGPL-3.0-or-later
 | 
			
		||||
# See Notices.txt for copyright information
 | 
			
		||||
[workspace]
 | 
			
		||||
resolver = "2"
 | 
			
		||||
members = ["crates/*"]
 | 
			
		||||
							
								
								
									
										157
									
								
								LICENSE.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										157
									
								
								LICENSE.md
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,157 @@
 | 
			
		|||
### GNU LESSER GENERAL PUBLIC LICENSE
 | 
			
		||||
 | 
			
		||||
Version 3, 29 June 2007
 | 
			
		||||
 | 
			
		||||
Copyright (C) 2007 Free Software Foundation, Inc.
 | 
			
		||||
<https://fsf.org/>
 | 
			
		||||
 | 
			
		||||
Everyone is permitted to copy and distribute verbatim copies of this
 | 
			
		||||
license document, but changing it is not allowed.
 | 
			
		||||
 | 
			
		||||
This version of the GNU Lesser General Public License incorporates the
 | 
			
		||||
terms and conditions of version 3 of the GNU General Public License,
 | 
			
		||||
supplemented by the additional permissions listed below.
 | 
			
		||||
 | 
			
		||||
#### 0. Additional Definitions.
 | 
			
		||||
 | 
			
		||||
As used herein, "this License" refers to version 3 of the GNU Lesser
 | 
			
		||||
General Public License, and the "GNU GPL" refers to version 3 of the
 | 
			
		||||
GNU General Public License.
 | 
			
		||||
 | 
			
		||||
"The Library" refers to a covered work governed by this License, other
 | 
			
		||||
than an Application or a Combined Work as defined below.
 | 
			
		||||
 | 
			
		||||
An "Application" is any work that makes use of an interface provided
 | 
			
		||||
by the Library, but which is not otherwise based on the Library.
 | 
			
		||||
Defining a subclass of a class defined by the Library is deemed a mode
 | 
			
		||||
of using an interface provided by the Library.
 | 
			
		||||
 | 
			
		||||
A "Combined Work" is a work produced by combining or linking an
 | 
			
		||||
Application with the Library. The particular version of the Library
 | 
			
		||||
with which the Combined Work was made is also called the "Linked
 | 
			
		||||
Version".
 | 
			
		||||
 | 
			
		||||
The "Minimal Corresponding Source" for a Combined Work means the
 | 
			
		||||
Corresponding Source for the Combined Work, excluding any source code
 | 
			
		||||
for portions of the Combined Work that, considered in isolation, are
 | 
			
		||||
based on the Application, and not on the Linked Version.
 | 
			
		||||
 | 
			
		||||
The "Corresponding Application Code" for a Combined Work means the
 | 
			
		||||
object code and/or source code for the Application, including any data
 | 
			
		||||
and utility programs needed for reproducing the Combined Work from the
 | 
			
		||||
Application, but excluding the System Libraries of the Combined Work.
 | 
			
		||||
 | 
			
		||||
#### 1. Exception to Section 3 of the GNU GPL.
 | 
			
		||||
 | 
			
		||||
You may convey a covered work under sections 3 and 4 of this License
 | 
			
		||||
without being bound by section 3 of the GNU GPL.
 | 
			
		||||
 | 
			
		||||
#### 2. Conveying Modified Versions.
 | 
			
		||||
 | 
			
		||||
If you modify a copy of the Library, and, in your modifications, a
 | 
			
		||||
facility refers to a function or data to be supplied by an Application
 | 
			
		||||
that uses the facility (other than as an argument passed when the
 | 
			
		||||
facility is invoked), then you may convey a copy of the modified
 | 
			
		||||
version:
 | 
			
		||||
 | 
			
		||||
-   a) under this License, provided that you make a good faith effort
 | 
			
		||||
    to ensure that, in the event an Application does not supply the
 | 
			
		||||
    function or data, the facility still operates, and performs
 | 
			
		||||
    whatever part of its purpose remains meaningful, or
 | 
			
		||||
-   b) under the GNU GPL, with none of the additional permissions of
 | 
			
		||||
    this License applicable to that copy.
 | 
			
		||||
 | 
			
		||||
#### 3. Object Code Incorporating Material from Library Header Files.
 | 
			
		||||
 | 
			
		||||
The object code form of an Application may incorporate material from a
 | 
			
		||||
header file that is part of the Library. You may convey such object
 | 
			
		||||
code under terms of your choice, provided that, if the incorporated
 | 
			
		||||
material is not limited to numerical parameters, data structure
 | 
			
		||||
layouts and accessors, or small macros, inline functions and templates
 | 
			
		||||
(ten or fewer lines in length), you do both of the following:
 | 
			
		||||
 | 
			
		||||
-   a) Give prominent notice with each copy of the object code that
 | 
			
		||||
    the Library is used in it and that the Library and its use are
 | 
			
		||||
    covered by this License.
 | 
			
		||||
-   b) Accompany the object code with a copy of the GNU GPL and this
 | 
			
		||||
    license document.
 | 
			
		||||
 | 
			
		||||
#### 4. Combined Works.
 | 
			
		||||
 | 
			
		||||
You may convey a Combined Work under terms of your choice that, taken
 | 
			
		||||
together, effectively do not restrict modification of the portions of
 | 
			
		||||
the Library contained in the Combined Work and reverse engineering for
 | 
			
		||||
debugging such modifications, if you also do each of the following:
 | 
			
		||||
 | 
			
		||||
-   a) Give prominent notice with each copy of the Combined Work that
 | 
			
		||||
    the Library is used in it and that the Library and its use are
 | 
			
		||||
    covered by this License.
 | 
			
		||||
-   b) Accompany the Combined Work with a copy of the GNU GPL and this
 | 
			
		||||
    license document.
 | 
			
		||||
-   c) For a Combined Work that displays copyright notices during
 | 
			
		||||
    execution, include the copyright notice for the Library among
 | 
			
		||||
    these notices, as well as a reference directing the user to the
 | 
			
		||||
    copies of the GNU GPL and this license document.
 | 
			
		||||
-   d) Do one of the following:
 | 
			
		||||
    -   0) Convey the Minimal Corresponding Source under the terms of
 | 
			
		||||
        this License, and the Corresponding Application Code in a form
 | 
			
		||||
        suitable for, and under terms that permit, the user to
 | 
			
		||||
        recombine or relink the Application with a modified version of
 | 
			
		||||
        the Linked Version to produce a modified Combined Work, in the
 | 
			
		||||
        manner specified by section 6 of the GNU GPL for conveying
 | 
			
		||||
        Corresponding Source.
 | 
			
		||||
    -   1) Use a suitable shared library mechanism for linking with
 | 
			
		||||
        the Library. A suitable mechanism is one that (a) uses at run
 | 
			
		||||
        time a copy of the Library already present on the user's
 | 
			
		||||
        computer system, and (b) will operate properly with a modified
 | 
			
		||||
        version of the Library that is interface-compatible with the
 | 
			
		||||
        Linked Version.
 | 
			
		||||
-   e) Provide Installation Information, but only if you would
 | 
			
		||||
    otherwise be required to provide such information under section 6
 | 
			
		||||
    of the GNU GPL, and only to the extent that such information is
 | 
			
		||||
    necessary to install and execute a modified version of the
 | 
			
		||||
    Combined Work produced by recombining or relinking the Application
 | 
			
		||||
    with a modified version of the Linked Version. (If you use option
 | 
			
		||||
    4d0, the Installation Information must accompany the Minimal
 | 
			
		||||
    Corresponding Source and Corresponding Application Code. If you
 | 
			
		||||
    use option 4d1, you must provide the Installation Information in
 | 
			
		||||
    the manner specified by section 6 of the GNU GPL for conveying
 | 
			
		||||
    Corresponding Source.)
 | 
			
		||||
 | 
			
		||||
#### 5. Combined Libraries.
 | 
			
		||||
 | 
			
		||||
You may place library facilities that are a work based on the Library
 | 
			
		||||
side by side in a single library together with other library
 | 
			
		||||
facilities that are not Applications and are not covered by this
 | 
			
		||||
License, and convey such a combined library under terms of your
 | 
			
		||||
choice, if you do both of the following:
 | 
			
		||||
 | 
			
		||||
-   a) Accompany the combined library with a copy of the same work
 | 
			
		||||
    based on the Library, uncombined with any other library
 | 
			
		||||
    facilities, conveyed under the terms of this License.
 | 
			
		||||
-   b) Give prominent notice with the combined library that part of it
 | 
			
		||||
    is a work based on the Library, and explaining where to find the
 | 
			
		||||
    accompanying uncombined form of the same work.
 | 
			
		||||
 | 
			
		||||
#### 6. Revised Versions of the GNU Lesser General Public License.
 | 
			
		||||
 | 
			
		||||
The Free Software Foundation may publish revised and/or new versions
 | 
			
		||||
of the GNU Lesser General Public License from time to time. Such new
 | 
			
		||||
versions will be similar in spirit to the present version, but may
 | 
			
		||||
differ in detail to address new problems or concerns.
 | 
			
		||||
 | 
			
		||||
Each version is given a distinguishing version number. If the Library
 | 
			
		||||
as you received it specifies that a certain numbered version of the
 | 
			
		||||
GNU Lesser General Public License "or any later version" applies to
 | 
			
		||||
it, you have the option of following the terms and conditions either
 | 
			
		||||
of that published version or of any later version published by the
 | 
			
		||||
Free Software Foundation. If the Library as you received it does not
 | 
			
		||||
specify a version number of the GNU Lesser General Public License, you
 | 
			
		||||
may choose any version of the GNU Lesser General Public License ever
 | 
			
		||||
published by the Free Software Foundation.
 | 
			
		||||
 | 
			
		||||
If the Library as you received it specifies that a proxy can decide
 | 
			
		||||
whether future versions of the GNU Lesser General Public License shall
 | 
			
		||||
apply, that proxy's public statement of acceptance of any version is
 | 
			
		||||
permanent authorization for you to choose that version for the
 | 
			
		||||
Library.
 | 
			
		||||
							
								
								
									
										16
									
								
								Notices.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								Notices.txt
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,16 @@
 | 
			
		|||
Copyright 2024 Jacob Lifshay
 | 
			
		||||
 | 
			
		||||
This file is part of Fayalite.
 | 
			
		||||
 | 
			
		||||
Fayalite is free software: you can redistribute it and/or modify
 | 
			
		||||
it under the terms of the GNU Lesser General Public License as published by
 | 
			
		||||
the Free Software Foundation, either version 3 of the License, or
 | 
			
		||||
(at your option) any later version.
 | 
			
		||||
 | 
			
		||||
Fayalite is distributed in the hope that it will be useful,
 | 
			
		||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
GNU Lesser General Public License for more details.
 | 
			
		||||
 | 
			
		||||
You should have received a copy of the GNU Lesser General Public License
 | 
			
		||||
along with Fayalite.  If not, see <https://www.gnu.org/licenses/>.
 | 
			
		||||
							
								
								
									
										18
									
								
								crates/fayalite-proc-macros-impl/Cargo.toml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								crates/fayalite-proc-macros-impl/Cargo.toml
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,18 @@
 | 
			
		|||
# SPDX-License-Identifier: LGPL-3.0-or-later
 | 
			
		||||
# See Notices.txt for copyright information
 | 
			
		||||
[package]
 | 
			
		||||
name = "fayalite-proc-macros-impl"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
edition = "2021"
 | 
			
		||||
workspace = "../.."
 | 
			
		||||
license = "LGPL-3.0-or-later"
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
base16ct = "0.2.0"
 | 
			
		||||
num-bigint = "0.4.4"
 | 
			
		||||
prettyplease = "0.2.20"
 | 
			
		||||
proc-macro2 = "1.0.78"
 | 
			
		||||
quote = "1.0.35"
 | 
			
		||||
sha2 = "0.10.8"
 | 
			
		||||
syn = { version = "2.0.53", features = ["full", "fold", "visit", "extra-traits"] }
 | 
			
		||||
tempfile = "3.10.1"
 | 
			
		||||
							
								
								
									
										4
									
								
								crates/fayalite-proc-macros-impl/build.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								crates/fayalite-proc-macros-impl/build.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,4 @@
 | 
			
		|||
// SPDX-License-Identifier: LGPL-3.0-or-later
 | 
			
		||||
// See Notices.txt for copyright information
 | 
			
		||||
// build.rs to make cargo set env!("OUT_DIR")
 | 
			
		||||
fn main() {}
 | 
			
		||||
							
								
								
									
										248
									
								
								crates/fayalite-proc-macros-impl/src/fold.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										248
									
								
								crates/fayalite-proc-macros-impl/src/fold.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,248 @@
 | 
			
		|||
// SPDX-License-Identifier: LGPL-3.0-or-later
 | 
			
		||||
// See Notices.txt for copyright information
 | 
			
		||||
pub(crate) trait DoFold<State: ?Sized + syn::fold::Fold> {
 | 
			
		||||
    fn do_fold(self, state: &mut State) -> Self;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: DoFold<State>, State: ?Sized + syn::fold::Fold> DoFold<State> for Box<T> {
 | 
			
		||||
    fn do_fold(mut self, state: &mut State) -> Self {
 | 
			
		||||
        *self = T::do_fold(*self, state);
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: DoFold<State>, State: ?Sized + syn::fold::Fold> DoFold<State> for Option<T> {
 | 
			
		||||
    fn do_fold(self, state: &mut State) -> Self {
 | 
			
		||||
        self.map(|v| T::do_fold(v, state))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: DoFold<State>, State: ?Sized + syn::fold::Fold> DoFold<State> for Vec<T> {
 | 
			
		||||
    fn do_fold(self, state: &mut State) -> Self {
 | 
			
		||||
        Vec::from_iter(self.into_iter().map(|v| T::do_fold(v, state)))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: DoFold<State>, P: DoFold<State>, State: ?Sized + syn::fold::Fold> DoFold<State>
 | 
			
		||||
    for Punctuated<T, P>
 | 
			
		||||
{
 | 
			
		||||
    fn do_fold(self, state: &mut State) -> Self {
 | 
			
		||||
        Punctuated::from_iter(self.into_pairs().map(|v| {
 | 
			
		||||
            let (v, p) = v.into_tuple().do_fold(state);
 | 
			
		||||
            Pair::new(v, p)
 | 
			
		||||
        }))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
macro_rules! impl_fold_tuple {
 | 
			
		||||
    ($($var0:ident: $T0:ident, $($var:ident: $T:ident,)*)?) => {
 | 
			
		||||
        $(impl_fold_tuple!($($var: $T,)*);)?
 | 
			
		||||
        impl_fold_tuple!(@impl $($var0: $T0, $($var: $T,)*)?);
 | 
			
		||||
    };
 | 
			
		||||
    (@impl $($var:ident: $T:ident,)*) => {
 | 
			
		||||
        impl<State: ?Sized + syn::fold::Fold, $($T: DoFold<State>,)*> DoFold<State> for ($($T,)*) {
 | 
			
		||||
            #[allow(clippy::unused_unit)]
 | 
			
		||||
            fn do_fold(self, state: &mut State) -> Self {
 | 
			
		||||
                let _ = state;
 | 
			
		||||
                let ($($var,)*) = self;
 | 
			
		||||
                $(let $var = $var.do_fold(state);)*
 | 
			
		||||
                ($($var,)*)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl_fold_tuple!(
 | 
			
		||||
    v0: T0,
 | 
			
		||||
    v1: T1,
 | 
			
		||||
    v2: T2,
 | 
			
		||||
    v3: T3,
 | 
			
		||||
    v4: T4,
 | 
			
		||||
    v5: T5,
 | 
			
		||||
    v6: T6,
 | 
			
		||||
    v7: T7,
 | 
			
		||||
    v8: T8,
 | 
			
		||||
    v9: T9,
 | 
			
		||||
    v10: T10,
 | 
			
		||||
    v11: T11,
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
macro_rules! no_op_fold {
 | 
			
		||||
    ($ty:ty) => {
 | 
			
		||||
        impl<State: ?Sized + syn::fold::Fold> $crate::fold::DoFold<State> for $ty {
 | 
			
		||||
            fn do_fold(self, _state: &mut State) -> Self {
 | 
			
		||||
                self
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) use no_op_fold;
 | 
			
		||||
 | 
			
		||||
macro_rules! impl_fold {
 | 
			
		||||
    (
 | 
			
		||||
        struct $Struct:ident<$($T:ident,)*> $(where ($($where:tt)*))? {
 | 
			
		||||
            $($field:ident: $field_ty:ty,)*
 | 
			
		||||
        }
 | 
			
		||||
    ) => {
 | 
			
		||||
        impl<State: ?Sized + syn::fold::Fold, $($T,)*> $crate::fold::DoFold<State> for $Struct<$($T,)*>
 | 
			
		||||
        where
 | 
			
		||||
            $($T: $crate::fold::DoFold<State>,)*
 | 
			
		||||
            $($where)*
 | 
			
		||||
        {
 | 
			
		||||
            fn do_fold(self, state: &mut State) -> Self {
 | 
			
		||||
                let _ = state;
 | 
			
		||||
                let Self {
 | 
			
		||||
                    $($field,)*
 | 
			
		||||
                } = self;
 | 
			
		||||
                Self {
 | 
			
		||||
                    $($field: <$field_ty as $crate::fold::DoFold<State>>::do_fold($field, state),)*
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
    (
 | 
			
		||||
        struct $Struct:ident<$($T:ident,)*>(
 | 
			
		||||
            $field0_ty:ty $(,)?
 | 
			
		||||
        )
 | 
			
		||||
        $(where ($($where:tt)*))?;
 | 
			
		||||
    ) => {
 | 
			
		||||
        impl<State: ?Sized + syn::fold::Fold, $($T,)*> $crate::fold::DoFold<State> for $Struct<$($T,)*>
 | 
			
		||||
        where
 | 
			
		||||
            $($T: $crate::fold::DoFold<State>,)*
 | 
			
		||||
            $($where)*
 | 
			
		||||
        {
 | 
			
		||||
            fn do_fold(self, state: &mut State) -> Self {
 | 
			
		||||
                let _ = state;
 | 
			
		||||
                let Self(
 | 
			
		||||
                    v0,
 | 
			
		||||
                ) = self;
 | 
			
		||||
                Self(
 | 
			
		||||
                    <$field0_ty as $crate::fold::DoFold<State>>::do_fold(v0, state),
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
    (
 | 
			
		||||
        enum $Enum:ident<$($T:ident,)*> $(where ($($where:tt)*))? {
 | 
			
		||||
            $($Variant:ident $({
 | 
			
		||||
                $($brace_field:ident: $brace_field_ty:ty,)*
 | 
			
		||||
            })?
 | 
			
		||||
            $((
 | 
			
		||||
                $($paren_field_ty:ty),* $(,)?
 | 
			
		||||
            ))?,)*
 | 
			
		||||
        }
 | 
			
		||||
    ) => {
 | 
			
		||||
        impl<State: ?Sized + syn::fold::Fold, $($T,)*> $crate::fold::DoFold<State> for $Enum<$($T,)*>
 | 
			
		||||
        where
 | 
			
		||||
            $($T: $crate::fold::DoFold<State>,)*
 | 
			
		||||
            $($where)*
 | 
			
		||||
        {
 | 
			
		||||
            fn do_fold(self, state: &mut State) -> Self {
 | 
			
		||||
                let _ = state;
 | 
			
		||||
                $crate::fold::impl_fold! {
 | 
			
		||||
                    @enum_variants self, state => ()
 | 
			
		||||
                    $($Variant $({
 | 
			
		||||
                        $($brace_field: $brace_field_ty,)*
 | 
			
		||||
                    })?
 | 
			
		||||
                    $((
 | 
			
		||||
                        $($paren_field_ty,)*
 | 
			
		||||
                    ))?,)*
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
    (
 | 
			
		||||
        @enum_variants $self:expr, $state:expr => ($($generated_arms:tt)*)
 | 
			
		||||
    ) => {
 | 
			
		||||
        match $self {
 | 
			
		||||
            $($generated_arms)*
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
    (
 | 
			
		||||
        @enum_variants $self:expr, $state:expr => ($($generated_arms:tt)*)
 | 
			
		||||
        $Variant:ident {
 | 
			
		||||
            $($field:tt: $field_ty:ty,)*
 | 
			
		||||
        },
 | 
			
		||||
        $($rest:tt)*
 | 
			
		||||
    ) => {
 | 
			
		||||
        $crate::fold::impl_fold! {
 | 
			
		||||
            @enum_variants $self, $state => (
 | 
			
		||||
                $($generated_arms)*
 | 
			
		||||
                Self::$Variant {
 | 
			
		||||
                    $($field,)*
 | 
			
		||||
                } => Self::$Variant {
 | 
			
		||||
                    $($field: <$field_ty as $crate::fold::DoFold<State>>::do_fold($field, $state),)*
 | 
			
		||||
                },
 | 
			
		||||
            )
 | 
			
		||||
            $($rest)*
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
    (
 | 
			
		||||
        @enum_variants $self:expr, $state:expr => ($($generated_arms:tt)*)
 | 
			
		||||
        $Variant:ident(
 | 
			
		||||
            $field0_ty:ty $(,)?
 | 
			
		||||
        ),
 | 
			
		||||
        $($rest:tt)*
 | 
			
		||||
    ) => {
 | 
			
		||||
        $crate::fold::impl_fold! {
 | 
			
		||||
            @enum_variants $self, $state => (
 | 
			
		||||
                $($generated_arms)*
 | 
			
		||||
                Self::$Variant(v0) => Self::$Variant(
 | 
			
		||||
                    <$field0_ty as $crate::fold::DoFold<State>>::do_fold(v0, $state),
 | 
			
		||||
                ),
 | 
			
		||||
            )
 | 
			
		||||
            $($rest)*
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) use impl_fold;
 | 
			
		||||
use syn::punctuated::{Pair, Punctuated};
 | 
			
		||||
 | 
			
		||||
macro_rules! forward_fold {
 | 
			
		||||
    ($ty:ty => $fn:ident) => {
 | 
			
		||||
        impl<State: syn::fold::Fold + ?Sized> DoFold<State> for $ty {
 | 
			
		||||
            fn do_fold(self, state: &mut State) -> Self {
 | 
			
		||||
                <State as syn::fold::Fold>::$fn(state, self)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
forward_fold!(syn::Attribute => fold_attribute);
 | 
			
		||||
forward_fold!(syn::AttrStyle => fold_attr_style);
 | 
			
		||||
forward_fold!(syn::Expr => fold_expr);
 | 
			
		||||
forward_fold!(syn::ExprArray => fold_expr_array);
 | 
			
		||||
forward_fold!(syn::ExprCall => fold_expr_call);
 | 
			
		||||
forward_fold!(syn::ExprIf => fold_expr_if);
 | 
			
		||||
forward_fold!(syn::ExprMatch => fold_expr_match);
 | 
			
		||||
forward_fold!(syn::ExprPath => fold_expr_path);
 | 
			
		||||
forward_fold!(syn::ExprStruct => fold_expr_struct);
 | 
			
		||||
forward_fold!(syn::ExprTuple => fold_expr_tuple);
 | 
			
		||||
forward_fold!(syn::Ident => fold_ident);
 | 
			
		||||
forward_fold!(syn::Member => fold_member);
 | 
			
		||||
forward_fold!(syn::Path => fold_path);
 | 
			
		||||
forward_fold!(syn::Type => fold_type);
 | 
			
		||||
forward_fold!(syn::TypePath => fold_type_path);
 | 
			
		||||
forward_fold!(syn::WherePredicate => fold_where_predicate);
 | 
			
		||||
no_op_fold!(syn::parse::Nothing);
 | 
			
		||||
no_op_fold!(syn::token::Brace);
 | 
			
		||||
no_op_fold!(syn::token::Bracket);
 | 
			
		||||
no_op_fold!(syn::token::Paren);
 | 
			
		||||
no_op_fold!(syn::Token![_]);
 | 
			
		||||
no_op_fold!(syn::Token![,]);
 | 
			
		||||
no_op_fold!(syn::Token![;]);
 | 
			
		||||
no_op_fold!(syn::Token![:]);
 | 
			
		||||
no_op_fold!(syn::Token![..]);
 | 
			
		||||
no_op_fold!(syn::Token![.]);
 | 
			
		||||
no_op_fold!(syn::Token![#]);
 | 
			
		||||
no_op_fold!(syn::Token![=]);
 | 
			
		||||
no_op_fold!(syn::Token![=>]);
 | 
			
		||||
no_op_fold!(syn::Token![|]);
 | 
			
		||||
no_op_fold!(syn::Token![enum]);
 | 
			
		||||
no_op_fold!(syn::Token![extern]);
 | 
			
		||||
no_op_fold!(syn::Token![let]);
 | 
			
		||||
no_op_fold!(syn::Token![mut]);
 | 
			
		||||
no_op_fold!(syn::Token![struct]);
 | 
			
		||||
no_op_fold!(syn::Token![where]);
 | 
			
		||||
							
								
								
									
										624
									
								
								crates/fayalite-proc-macros-impl/src/lib.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										624
									
								
								crates/fayalite-proc-macros-impl/src/lib.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,624 @@
 | 
			
		|||
// SPDX-License-Identifier: LGPL-3.0-or-later
 | 
			
		||||
// See Notices.txt for copyright information
 | 
			
		||||
#![cfg_attr(test, recursion_limit = "512")]
 | 
			
		||||
use proc_macro2::{Span, TokenStream};
 | 
			
		||||
use quote::{quote, ToTokens};
 | 
			
		||||
use std::io::{ErrorKind, Write};
 | 
			
		||||
use syn::{
 | 
			
		||||
    bracketed, parenthesized,
 | 
			
		||||
    parse::{Parse, ParseStream, Parser},
 | 
			
		||||
    parse_quote, AttrStyle, Attribute, Error, Item, Token,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
mod fold;
 | 
			
		||||
mod module;
 | 
			
		||||
mod value_derive_common;
 | 
			
		||||
mod value_derive_enum;
 | 
			
		||||
mod value_derive_struct;
 | 
			
		||||
 | 
			
		||||
mod kw {
 | 
			
		||||
    pub(crate) use syn::token::{
 | 
			
		||||
        Enum as enum_, Extern as extern_, Struct as struct_, Where as where_,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    macro_rules! custom_keyword {
 | 
			
		||||
        ($kw:ident) => {
 | 
			
		||||
            syn::custom_keyword!($kw);
 | 
			
		||||
 | 
			
		||||
            impl quote::IdentFragment for $kw {
 | 
			
		||||
                fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
 | 
			
		||||
                    f.write_str(stringify!($kw))
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                fn span(&self) -> Option<proc_macro2::Span> {
 | 
			
		||||
                    Some(self.span)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            crate::fold::no_op_fold!($kw);
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    custom_keyword!(clock_domain);
 | 
			
		||||
    custom_keyword!(connect_inexact);
 | 
			
		||||
    custom_keyword!(fixed_type);
 | 
			
		||||
    custom_keyword!(flip);
 | 
			
		||||
    custom_keyword!(hdl);
 | 
			
		||||
    custom_keyword!(input);
 | 
			
		||||
    custom_keyword!(instance);
 | 
			
		||||
    custom_keyword!(m);
 | 
			
		||||
    custom_keyword!(memory);
 | 
			
		||||
    custom_keyword!(memory_array);
 | 
			
		||||
    custom_keyword!(memory_with_init);
 | 
			
		||||
    custom_keyword!(no_reset);
 | 
			
		||||
    custom_keyword!(outline_generated);
 | 
			
		||||
    custom_keyword!(output);
 | 
			
		||||
    custom_keyword!(reg_builder);
 | 
			
		||||
    custom_keyword!(reset);
 | 
			
		||||
    custom_keyword!(reset_default);
 | 
			
		||||
    custom_keyword!(skip);
 | 
			
		||||
    custom_keyword!(target);
 | 
			
		||||
    custom_keyword!(wire);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Pound = Token![#]; // work around https://github.com/rust-lang/rust/issues/50676
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug)]
 | 
			
		||||
pub(crate) struct HdlAttr<T> {
 | 
			
		||||
    pub(crate) pound_token: Pound,
 | 
			
		||||
    pub(crate) style: AttrStyle,
 | 
			
		||||
    pub(crate) bracket_token: syn::token::Bracket,
 | 
			
		||||
    pub(crate) hdl: kw::hdl,
 | 
			
		||||
    pub(crate) paren_token: Option<syn::token::Paren>,
 | 
			
		||||
    pub(crate) body: T,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
crate::fold::impl_fold! {
 | 
			
		||||
    struct HdlAttr<T,> {
 | 
			
		||||
        pound_token: Pound,
 | 
			
		||||
        style: AttrStyle,
 | 
			
		||||
        bracket_token: syn::token::Bracket,
 | 
			
		||||
        hdl: kw::hdl,
 | 
			
		||||
        paren_token: Option<syn::token::Paren>,
 | 
			
		||||
        body: T,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[allow(dead_code)]
 | 
			
		||||
impl<T> HdlAttr<T> {
 | 
			
		||||
    pub(crate) fn split_body(self) -> (HdlAttr<()>, T) {
 | 
			
		||||
        let Self {
 | 
			
		||||
            pound_token,
 | 
			
		||||
            style,
 | 
			
		||||
            bracket_token,
 | 
			
		||||
            hdl,
 | 
			
		||||
            paren_token,
 | 
			
		||||
            body,
 | 
			
		||||
        } = self;
 | 
			
		||||
        (
 | 
			
		||||
            HdlAttr {
 | 
			
		||||
                pound_token,
 | 
			
		||||
                style,
 | 
			
		||||
                bracket_token,
 | 
			
		||||
                hdl,
 | 
			
		||||
                paren_token,
 | 
			
		||||
                body: (),
 | 
			
		||||
            },
 | 
			
		||||
            body,
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
    pub(crate) fn replace_body<T2>(self, body: T2) -> HdlAttr<T2> {
 | 
			
		||||
        let Self {
 | 
			
		||||
            pound_token,
 | 
			
		||||
            style,
 | 
			
		||||
            bracket_token,
 | 
			
		||||
            hdl,
 | 
			
		||||
            paren_token,
 | 
			
		||||
            body: _,
 | 
			
		||||
        } = self;
 | 
			
		||||
        HdlAttr {
 | 
			
		||||
            pound_token,
 | 
			
		||||
            style,
 | 
			
		||||
            bracket_token,
 | 
			
		||||
            hdl,
 | 
			
		||||
            paren_token,
 | 
			
		||||
            body,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub(crate) fn as_ref(&self) -> HdlAttr<&T> {
 | 
			
		||||
        let Self {
 | 
			
		||||
            pound_token,
 | 
			
		||||
            style,
 | 
			
		||||
            bracket_token,
 | 
			
		||||
            hdl,
 | 
			
		||||
            paren_token,
 | 
			
		||||
            ref body,
 | 
			
		||||
        } = *self;
 | 
			
		||||
        HdlAttr {
 | 
			
		||||
            pound_token,
 | 
			
		||||
            style,
 | 
			
		||||
            bracket_token,
 | 
			
		||||
            hdl,
 | 
			
		||||
            paren_token,
 | 
			
		||||
            body,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub(crate) fn try_map<R, E, F: FnOnce(T) -> Result<R, E>>(self, f: F) -> Result<HdlAttr<R>, E> {
 | 
			
		||||
        let Self {
 | 
			
		||||
            pound_token,
 | 
			
		||||
            style,
 | 
			
		||||
            bracket_token,
 | 
			
		||||
            hdl,
 | 
			
		||||
            paren_token,
 | 
			
		||||
            body,
 | 
			
		||||
        } = self;
 | 
			
		||||
        Ok(HdlAttr {
 | 
			
		||||
            pound_token,
 | 
			
		||||
            style,
 | 
			
		||||
            bracket_token,
 | 
			
		||||
            hdl,
 | 
			
		||||
            paren_token,
 | 
			
		||||
            body: f(body)?,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
    pub(crate) fn map<R, F: FnOnce(T) -> R>(self, f: F) -> HdlAttr<R> {
 | 
			
		||||
        let Self {
 | 
			
		||||
            pound_token,
 | 
			
		||||
            style,
 | 
			
		||||
            bracket_token,
 | 
			
		||||
            hdl,
 | 
			
		||||
            paren_token,
 | 
			
		||||
            body,
 | 
			
		||||
        } = self;
 | 
			
		||||
        HdlAttr {
 | 
			
		||||
            pound_token,
 | 
			
		||||
            style,
 | 
			
		||||
            bracket_token,
 | 
			
		||||
            hdl,
 | 
			
		||||
            paren_token,
 | 
			
		||||
            body: f(body),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    fn to_attr(&self) -> Attribute
 | 
			
		||||
    where
 | 
			
		||||
        T: ToTokens,
 | 
			
		||||
    {
 | 
			
		||||
        parse_quote! { #self }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: Default> Default for HdlAttr<T> {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        T::default().into()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T> From<T> for HdlAttr<T> {
 | 
			
		||||
    fn from(body: T) -> Self {
 | 
			
		||||
        HdlAttr {
 | 
			
		||||
            pound_token: Default::default(),
 | 
			
		||||
            style: AttrStyle::Outer,
 | 
			
		||||
            bracket_token: Default::default(),
 | 
			
		||||
            hdl: Default::default(),
 | 
			
		||||
            paren_token: Default::default(),
 | 
			
		||||
            body,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: ToTokens> ToTokens for HdlAttr<T> {
 | 
			
		||||
    fn to_tokens(&self, tokens: &mut TokenStream) {
 | 
			
		||||
        self.pound_token.to_tokens(tokens);
 | 
			
		||||
        match self.style {
 | 
			
		||||
            AttrStyle::Inner(style) => style.to_tokens(tokens),
 | 
			
		||||
            AttrStyle::Outer => {}
 | 
			
		||||
        };
 | 
			
		||||
        self.bracket_token.surround(tokens, |tokens| {
 | 
			
		||||
            self.hdl.to_tokens(tokens);
 | 
			
		||||
            match self.paren_token {
 | 
			
		||||
                Some(paren_token) => {
 | 
			
		||||
                    paren_token.surround(tokens, |tokens| self.body.to_tokens(tokens))
 | 
			
		||||
                }
 | 
			
		||||
                None => {
 | 
			
		||||
                    let body = self.body.to_token_stream();
 | 
			
		||||
                    if !body.is_empty() {
 | 
			
		||||
                        syn::token::Paren(self.hdl.span)
 | 
			
		||||
                            .surround(tokens, |tokens| tokens.extend([body]));
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn is_hdl_attr(attr: &Attribute) -> bool {
 | 
			
		||||
    attr.path().is_ident("hdl")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: Parse> HdlAttr<T> {
 | 
			
		||||
    fn parse_and_take_attr(attrs: &mut Vec<Attribute>) -> syn::Result<Option<Self>> {
 | 
			
		||||
        let mut retval = None;
 | 
			
		||||
        let mut errors = Errors::new();
 | 
			
		||||
        attrs.retain(|attr| {
 | 
			
		||||
            if is_hdl_attr(attr) {
 | 
			
		||||
                if retval.is_some() {
 | 
			
		||||
                    errors.push(Error::new_spanned(attr, "more than one #[hdl] attribute"));
 | 
			
		||||
                }
 | 
			
		||||
                errors.unwrap_or_default(Self::parse_attr(attr).map(|v| retval = Some(v)));
 | 
			
		||||
                false
 | 
			
		||||
            } else {
 | 
			
		||||
                true
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        errors.finish()?;
 | 
			
		||||
        Ok(retval)
 | 
			
		||||
    }
 | 
			
		||||
    fn parse_and_leave_attr(attrs: &[Attribute]) -> syn::Result<Option<Self>> {
 | 
			
		||||
        let mut retval = None;
 | 
			
		||||
        let mut errors = Errors::new();
 | 
			
		||||
        for attr in attrs {
 | 
			
		||||
            if is_hdl_attr(attr) {
 | 
			
		||||
                if retval.is_some() {
 | 
			
		||||
                    errors.push(Error::new_spanned(attr, "more than one #[hdl] attribute"));
 | 
			
		||||
                }
 | 
			
		||||
                errors.unwrap_or_default(Self::parse_attr(attr).map(|v| retval = Some(v)));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        errors.finish()?;
 | 
			
		||||
        Ok(retval)
 | 
			
		||||
    }
 | 
			
		||||
    fn parse_attr(attr: &Attribute) -> syn::Result<Self> {
 | 
			
		||||
        match attr.style {
 | 
			
		||||
            AttrStyle::Outer => Parser::parse2(Self::parse_outer, attr.to_token_stream()),
 | 
			
		||||
            AttrStyle::Inner(_) => Parser::parse2(Self::parse_inner, attr.to_token_stream()),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    fn parse_starting_with_brackets(
 | 
			
		||||
        pound_token: Token![#],
 | 
			
		||||
        style: AttrStyle,
 | 
			
		||||
        input: ParseStream,
 | 
			
		||||
    ) -> syn::Result<Self> {
 | 
			
		||||
        let bracket_content;
 | 
			
		||||
        let bracket_token = bracketed!(bracket_content in input);
 | 
			
		||||
        let hdl = bracket_content.parse()?;
 | 
			
		||||
        let paren_content;
 | 
			
		||||
        let body;
 | 
			
		||||
        let paren_token;
 | 
			
		||||
        if bracket_content.is_empty() {
 | 
			
		||||
            body = match syn::parse2(TokenStream::default()) {
 | 
			
		||||
                Ok(body) => body,
 | 
			
		||||
                Err(_) => {
 | 
			
		||||
                    parenthesized!(paren_content in bracket_content);
 | 
			
		||||
                    unreachable!();
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
            paren_token = None;
 | 
			
		||||
        } else {
 | 
			
		||||
            paren_token = Some(parenthesized!(paren_content in bracket_content));
 | 
			
		||||
            body = paren_content.parse()?;
 | 
			
		||||
        }
 | 
			
		||||
        Ok(Self {
 | 
			
		||||
            pound_token,
 | 
			
		||||
            style,
 | 
			
		||||
            bracket_token,
 | 
			
		||||
            hdl,
 | 
			
		||||
            paren_token,
 | 
			
		||||
            body,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
    fn parse_inner(input: ParseStream) -> syn::Result<Self> {
 | 
			
		||||
        let pound_token = input.parse()?;
 | 
			
		||||
        let style = AttrStyle::Inner(input.parse()?);
 | 
			
		||||
        Self::parse_starting_with_brackets(pound_token, style, input)
 | 
			
		||||
    }
 | 
			
		||||
    fn parse_outer(input: ParseStream) -> syn::Result<Self> {
 | 
			
		||||
        let pound_token = input.parse()?;
 | 
			
		||||
        let style = AttrStyle::Outer;
 | 
			
		||||
        Self::parse_starting_with_brackets(pound_token, style, input)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) struct Errors {
 | 
			
		||||
    error: Option<Error>,
 | 
			
		||||
    finished: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Drop for Errors {
 | 
			
		||||
    fn drop(&mut self) {
 | 
			
		||||
        if !std::thread::panicking() {
 | 
			
		||||
            assert!(self.finished, "didn't run finish");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Errors {
 | 
			
		||||
    pub(crate) fn new() -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            error: None,
 | 
			
		||||
            finished: false,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub(crate) fn push(&mut self, e: Error) -> &mut Self {
 | 
			
		||||
        match self.error {
 | 
			
		||||
            Some(ref mut old) => old.combine(e),
 | 
			
		||||
            None => self.error = Some(e),
 | 
			
		||||
        }
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
    pub(crate) fn push_result(&mut self, e: syn::Result<()>) -> &mut Self {
 | 
			
		||||
        self.ok(e);
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
    pub(crate) fn error(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        tokens: impl ToTokens,
 | 
			
		||||
        message: impl std::fmt::Display,
 | 
			
		||||
    ) -> &mut Self {
 | 
			
		||||
        self.push(Error::new_spanned(tokens, message));
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
    pub(crate) fn ok<T>(&mut self, v: syn::Result<T>) -> Option<T> {
 | 
			
		||||
        match v {
 | 
			
		||||
            Ok(v) => Some(v),
 | 
			
		||||
            Err(e) => {
 | 
			
		||||
                self.push(e);
 | 
			
		||||
                None
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub(crate) fn unwrap_or_else<T>(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        v: syn::Result<T>,
 | 
			
		||||
        fallback: impl FnOnce() -> T,
 | 
			
		||||
    ) -> T {
 | 
			
		||||
        match v {
 | 
			
		||||
            Ok(v) => v,
 | 
			
		||||
            Err(e) => {
 | 
			
		||||
                self.push(e);
 | 
			
		||||
                fallback()
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub(crate) fn unwrap_or<T>(&mut self, v: syn::Result<T>, fallback: T) -> T {
 | 
			
		||||
        self.unwrap_or_else(v, || fallback)
 | 
			
		||||
    }
 | 
			
		||||
    pub(crate) fn unwrap_or_default<T: Default>(&mut self, v: syn::Result<T>) -> T {
 | 
			
		||||
        self.unwrap_or_else(v, T::default)
 | 
			
		||||
    }
 | 
			
		||||
    pub(crate) fn finish(&mut self) -> syn::Result<()> {
 | 
			
		||||
        self.finished = true;
 | 
			
		||||
        match self.error.take() {
 | 
			
		||||
            Some(e) => Err(e),
 | 
			
		||||
            None => Ok(()),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for Errors {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        Self::new()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
macro_rules! impl_extra_traits_for_options {
 | 
			
		||||
    (
 | 
			
		||||
        #[no_ident_fragment]
 | 
			
		||||
        $enum_vis:vis enum $option_enum_name:ident {
 | 
			
		||||
            $($Variant:ident($key:ident),)*
 | 
			
		||||
        }
 | 
			
		||||
    ) => {
 | 
			
		||||
        impl Copy for $option_enum_name {}
 | 
			
		||||
    };
 | 
			
		||||
    (
 | 
			
		||||
        $enum_vis:vis enum $option_enum_name:ident {
 | 
			
		||||
            $($Variant:ident($key:ident),)*
 | 
			
		||||
        }
 | 
			
		||||
    ) => {
 | 
			
		||||
        impl Copy for $option_enum_name {}
 | 
			
		||||
 | 
			
		||||
        impl quote::IdentFragment for $option_enum_name {
 | 
			
		||||
            fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
 | 
			
		||||
                let _ = f;
 | 
			
		||||
                match *self {
 | 
			
		||||
                    $(Self::$Variant(ref v) => quote::IdentFragment::fmt(&v.0, f),)*
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            fn span(&self) -> Option<proc_macro2::Span> {
 | 
			
		||||
                match *self {
 | 
			
		||||
                    $(Self::$Variant(ref v) => quote::IdentFragment::span(&v.0),)*
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        impl $option_enum_name {
 | 
			
		||||
            #[allow(dead_code)]
 | 
			
		||||
            $enum_vis fn span(&self) -> proc_macro2::Span {
 | 
			
		||||
                quote::IdentFragment::span(self).unwrap()
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
    (
 | 
			
		||||
        $(#[no_ident_fragment])?
 | 
			
		||||
        $enum_vis:vis enum $option_enum_name:ident {
 | 
			
		||||
            $($Variant:ident($key:ident $(, $value:ty)?),)*
 | 
			
		||||
        }
 | 
			
		||||
    ) => {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) use impl_extra_traits_for_options;
 | 
			
		||||
 | 
			
		||||
macro_rules! options {
 | 
			
		||||
    (
 | 
			
		||||
        #[options = $options_name:ident]
 | 
			
		||||
        $(#[$($enum_meta:tt)*])*
 | 
			
		||||
        $enum_vis:vis enum $option_enum_name:ident {
 | 
			
		||||
            $($Variant:ident($key:ident $(, $value:ty)?),)*
 | 
			
		||||
        }
 | 
			
		||||
    ) => {
 | 
			
		||||
        crate::options! {
 | 
			
		||||
            $(#[$($enum_meta)*])*
 | 
			
		||||
            $enum_vis enum $option_enum_name {
 | 
			
		||||
                $($Variant($key $(, $value)?),)*
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[derive(Clone, Debug, Default)]
 | 
			
		||||
        $enum_vis struct $options_name {
 | 
			
		||||
            $($enum_vis $key: Option<(crate::kw::$key, $(syn::token::Paren, $value)?)>,)*
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        crate::fold::impl_fold! {
 | 
			
		||||
            struct $options_name<> {
 | 
			
		||||
                $($key: Option<(crate::kw::$key, $(syn::token::Paren, $value)?)>,)*
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        impl syn::parse::Parse for $options_name {
 | 
			
		||||
            fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
 | 
			
		||||
                #![allow(unused_mut, unused_variables, unreachable_code)]
 | 
			
		||||
                let mut retval = Self::default();
 | 
			
		||||
                while !input.is_empty() {
 | 
			
		||||
                    let old_input = input.fork();
 | 
			
		||||
                    match input.parse::<$option_enum_name>()? {
 | 
			
		||||
                        $($option_enum_name::$Variant(v) => {
 | 
			
		||||
                            if retval.$key.replace(v).is_some() {
 | 
			
		||||
                                return Err(old_input.error(concat!("duplicate ", stringify!($key), " option")));
 | 
			
		||||
                            }
 | 
			
		||||
                        })*
 | 
			
		||||
                    }
 | 
			
		||||
                    if input.is_empty() {
 | 
			
		||||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
                    input.parse::<syn::Token![,]>()?;
 | 
			
		||||
                }
 | 
			
		||||
                Ok(retval)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        impl quote::ToTokens for $options_name {
 | 
			
		||||
            #[allow(unused_mut, unused_variables, unused_assignments)]
 | 
			
		||||
            fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
 | 
			
		||||
                let mut separator: Option<syn::Token![,]> = None;
 | 
			
		||||
                $(if let Some(v) = &self.$key {
 | 
			
		||||
                    separator.to_tokens(tokens);
 | 
			
		||||
                    separator = Some(Default::default());
 | 
			
		||||
                    v.0.to_tokens(tokens);
 | 
			
		||||
                    $(v.1.surround(tokens, |tokens| <$value as quote::ToTokens>::to_tokens(&v.2, tokens));)?
 | 
			
		||||
                })*
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
    (
 | 
			
		||||
        $(#[$($enum_meta:tt)*])*
 | 
			
		||||
        $enum_vis:vis enum $option_enum_name:ident {
 | 
			
		||||
            $($Variant:ident($key:ident $(, $value:ty)?),)*
 | 
			
		||||
        }
 | 
			
		||||
    ) => {
 | 
			
		||||
        #[derive(Clone, Debug)]
 | 
			
		||||
        $enum_vis enum $option_enum_name {
 | 
			
		||||
            $($Variant((crate::kw::$key, $(syn::token::Paren, $value)?)),)*
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        crate::impl_extra_traits_for_options! {
 | 
			
		||||
            $(#[$($enum_meta)*])*
 | 
			
		||||
            $enum_vis enum $option_enum_name {
 | 
			
		||||
                $($Variant($key $(, $value)?),)*
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        crate::fold::impl_fold! {
 | 
			
		||||
            enum $option_enum_name<> {
 | 
			
		||||
                $($Variant((crate::kw::$key, $(syn::token::Paren, $value)?)),)*
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        impl syn::parse::Parse for $option_enum_name {
 | 
			
		||||
            fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
 | 
			
		||||
                let lookahead = input.lookahead1();
 | 
			
		||||
                $(
 | 
			
		||||
                    if lookahead.peek(crate::kw::$key) {
 | 
			
		||||
                        #[allow(unused_variables)]
 | 
			
		||||
                        let paren_content: syn::parse::ParseBuffer;
 | 
			
		||||
                        return Ok($option_enum_name::$Variant((
 | 
			
		||||
                            input.parse()?,
 | 
			
		||||
                            $(
 | 
			
		||||
                                syn::parenthesized!(paren_content in input),
 | 
			
		||||
                                paren_content.parse::<$value>()?,
 | 
			
		||||
                            )?
 | 
			
		||||
                        )));
 | 
			
		||||
                    }
 | 
			
		||||
                )*
 | 
			
		||||
                Err(lookahead.error())
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        impl quote::ToTokens for $option_enum_name {
 | 
			
		||||
            fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
 | 
			
		||||
                let _ = tokens;
 | 
			
		||||
                match *self {
 | 
			
		||||
                    $($option_enum_name::$Variant(ref v) => {
 | 
			
		||||
                        v.0.to_tokens(tokens);
 | 
			
		||||
                        $(
 | 
			
		||||
                            let value: &$value = &v.2;
 | 
			
		||||
                            v.1.surround(tokens, |tokens| value.to_tokens(tokens));
 | 
			
		||||
                        )?
 | 
			
		||||
                    })*
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) use options;
 | 
			
		||||
 | 
			
		||||
pub(crate) fn outline_generated(contents: TokenStream, prefix: &str) -> TokenStream {
 | 
			
		||||
    let out_dir = env!("OUT_DIR");
 | 
			
		||||
    let mut file = tempfile::Builder::new()
 | 
			
		||||
        .prefix(prefix)
 | 
			
		||||
        .rand_bytes(6)
 | 
			
		||||
        .suffix(".tmp.rs")
 | 
			
		||||
        .tempfile_in(out_dir)
 | 
			
		||||
        .unwrap();
 | 
			
		||||
    let contents = prettyplease::unparse(&parse_quote! { #contents });
 | 
			
		||||
    let hash = <sha2::Sha256 as sha2::Digest>::digest(&contents);
 | 
			
		||||
    let hash = base16ct::HexDisplay(&hash[..5]);
 | 
			
		||||
    file.write_all(contents.as_bytes()).unwrap();
 | 
			
		||||
    let dest_file = std::path::Path::new(out_dir).join(format!("{prefix}{hash:x}.rs"));
 | 
			
		||||
    // don't write if it already exists so cargo doesn't try to recompile constantly.
 | 
			
		||||
    match file.persist_noclobber(&dest_file) {
 | 
			
		||||
        Err(e) if e.error.kind() == ErrorKind::AlreadyExists => {}
 | 
			
		||||
        e => {
 | 
			
		||||
            e.unwrap();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    eprintln!("generated {}", dest_file.display());
 | 
			
		||||
    let dest_file = dest_file.to_str().unwrap();
 | 
			
		||||
 | 
			
		||||
    quote! {
 | 
			
		||||
        include!(#dest_file);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn module(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> {
 | 
			
		||||
    let options = syn::parse2::<module::ConfigOptions>(attr)?;
 | 
			
		||||
    let options = HdlAttr::from(options);
 | 
			
		||||
    let func = syn::parse2::<module::ModuleFn>(quote! { #options #item })?;
 | 
			
		||||
    let mut contents = func.generate();
 | 
			
		||||
    if options.body.outline_generated.is_some() {
 | 
			
		||||
        contents = outline_generated(contents, "module-");
 | 
			
		||||
    }
 | 
			
		||||
    Ok(contents)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn value_derive(item: TokenStream) -> syn::Result<TokenStream> {
 | 
			
		||||
    let item = syn::parse2::<Item>(item)?;
 | 
			
		||||
    match item {
 | 
			
		||||
        Item::Enum(item) => value_derive_enum::value_derive_enum(item),
 | 
			
		||||
        Item::Struct(item) => value_derive_struct::value_derive_struct(item),
 | 
			
		||||
        _ => Err(syn::Error::new(
 | 
			
		||||
            Span::call_site(),
 | 
			
		||||
            "derive(Value) can only be used on structs or enums",
 | 
			
		||||
        )),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										282
									
								
								crates/fayalite-proc-macros-impl/src/module.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										282
									
								
								crates/fayalite-proc-macros-impl/src/module.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,282 @@
 | 
			
		|||
// SPDX-License-Identifier: LGPL-3.0-or-later
 | 
			
		||||
// See Notices.txt for copyright information
 | 
			
		||||
use crate::{
 | 
			
		||||
    is_hdl_attr,
 | 
			
		||||
    module::transform_body::{HdlLet, HdlLetKindIO},
 | 
			
		||||
    options, Errors, HdlAttr,
 | 
			
		||||
};
 | 
			
		||||
use proc_macro2::TokenStream;
 | 
			
		||||
use quote::{format_ident, quote, quote_spanned, ToTokens};
 | 
			
		||||
use syn::{
 | 
			
		||||
    parse::{Parse, ParseStream},
 | 
			
		||||
    parse_quote,
 | 
			
		||||
    visit::{visit_pat, Visit},
 | 
			
		||||
    Attribute, Block, Error, FnArg, Ident, ItemFn, ItemStruct, ReturnType, Signature, Visibility,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
mod transform_body;
 | 
			
		||||
 | 
			
		||||
options! {
 | 
			
		||||
    #[options = ConfigOptions]
 | 
			
		||||
    #[no_ident_fragment]
 | 
			
		||||
    pub(crate) enum ConfigOption {
 | 
			
		||||
        OutlineGenerated(outline_generated),
 | 
			
		||||
        Extern(extern_),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
options! {
 | 
			
		||||
    pub(crate) enum ModuleIOKind {
 | 
			
		||||
        Input(input),
 | 
			
		||||
        Output(output),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) fn check_name_conflicts_with_module_builder(name: &Ident) -> syn::Result<()> {
 | 
			
		||||
    if name == "m" {
 | 
			
		||||
        Err(Error::new_spanned(
 | 
			
		||||
            name,
 | 
			
		||||
            "name conflicts with implicit `m: &mut ModuleBuilder<_>`",
 | 
			
		||||
        ))
 | 
			
		||||
    } else {
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) struct CheckNameConflictsWithModuleBuilderVisitor<'a> {
 | 
			
		||||
    pub(crate) errors: &'a mut Errors,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Visit<'_> for CheckNameConflictsWithModuleBuilderVisitor<'_> {
 | 
			
		||||
    // TODO: change this to only check for identifiers defining new variables
 | 
			
		||||
    fn visit_ident(&mut self, node: &Ident) {
 | 
			
		||||
        self.errors
 | 
			
		||||
            .push_result(check_name_conflicts_with_module_builder(node));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn retain_struct_attrs<F: FnMut(&Attribute) -> bool>(item: &mut ItemStruct, mut f: F) {
 | 
			
		||||
    item.attrs.retain(&mut f);
 | 
			
		||||
    for field in item.fields.iter_mut() {
 | 
			
		||||
        field.attrs.retain(&mut f);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) type ModuleIO = HdlLet<HdlLetKindIO>;
 | 
			
		||||
 | 
			
		||||
pub(crate) struct ModuleFn {
 | 
			
		||||
    attrs: Vec<Attribute>,
 | 
			
		||||
    config_options: HdlAttr<ConfigOptions>,
 | 
			
		||||
    module_kind: ModuleKind,
 | 
			
		||||
    vis: Visibility,
 | 
			
		||||
    sig: Signature,
 | 
			
		||||
    block: Box<Block>,
 | 
			
		||||
    io: Vec<ModuleIO>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
 | 
			
		||||
pub(crate) enum ModuleKind {
 | 
			
		||||
    Extern,
 | 
			
		||||
    Normal,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Parse for ModuleFn {
 | 
			
		||||
    fn parse(input: ParseStream) -> syn::Result<Self> {
 | 
			
		||||
        let ItemFn {
 | 
			
		||||
            mut attrs,
 | 
			
		||||
            vis,
 | 
			
		||||
            mut sig,
 | 
			
		||||
            block,
 | 
			
		||||
        } = input.parse()?;
 | 
			
		||||
        let Signature {
 | 
			
		||||
            ref constness,
 | 
			
		||||
            ref asyncness,
 | 
			
		||||
            ref unsafety,
 | 
			
		||||
            ref abi,
 | 
			
		||||
            fn_token: _,
 | 
			
		||||
            ident: _,
 | 
			
		||||
            ref generics,
 | 
			
		||||
            paren_token: _,
 | 
			
		||||
            ref mut inputs,
 | 
			
		||||
            ref variadic,
 | 
			
		||||
            ref output,
 | 
			
		||||
        } = sig;
 | 
			
		||||
        let mut errors = Errors::new();
 | 
			
		||||
        let config_options = errors
 | 
			
		||||
            .unwrap_or_default(HdlAttr::parse_and_take_attr(&mut attrs))
 | 
			
		||||
            .unwrap_or_default();
 | 
			
		||||
        let ConfigOptions {
 | 
			
		||||
            outline_generated: _,
 | 
			
		||||
            extern_,
 | 
			
		||||
        } = config_options.body;
 | 
			
		||||
        let module_kind = match extern_ {
 | 
			
		||||
            Some(_) => ModuleKind::Extern,
 | 
			
		||||
            None => ModuleKind::Normal,
 | 
			
		||||
        };
 | 
			
		||||
        for fn_arg in inputs {
 | 
			
		||||
            match fn_arg {
 | 
			
		||||
                FnArg::Receiver(_) => {
 | 
			
		||||
                    errors.push(syn::Error::new_spanned(fn_arg, "self not allowed here"));
 | 
			
		||||
                }
 | 
			
		||||
                FnArg::Typed(fn_arg) => {
 | 
			
		||||
                    visit_pat(
 | 
			
		||||
                        &mut CheckNameConflictsWithModuleBuilderVisitor {
 | 
			
		||||
                            errors: &mut errors,
 | 
			
		||||
                        },
 | 
			
		||||
                        &fn_arg.pat,
 | 
			
		||||
                    );
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if let Some(constness) = constness {
 | 
			
		||||
            errors.push(syn::Error::new_spanned(constness, "const not allowed here"));
 | 
			
		||||
        }
 | 
			
		||||
        if let Some(asyncness) = asyncness {
 | 
			
		||||
            errors.push(syn::Error::new_spanned(asyncness, "async not allowed here"));
 | 
			
		||||
        }
 | 
			
		||||
        if let Some(unsafety) = unsafety {
 | 
			
		||||
            errors.push(syn::Error::new_spanned(unsafety, "unsafe not allowed here"));
 | 
			
		||||
        }
 | 
			
		||||
        if let Some(abi) = abi {
 | 
			
		||||
            errors.push(syn::Error::new_spanned(abi, "extern not allowed here"));
 | 
			
		||||
        }
 | 
			
		||||
        if !generics.params.is_empty() {
 | 
			
		||||
            errors.push(syn::Error::new_spanned(
 | 
			
		||||
                &generics.params,
 | 
			
		||||
                "generics are not supported yet",
 | 
			
		||||
            ));
 | 
			
		||||
        }
 | 
			
		||||
        if let Some(variadic) = variadic {
 | 
			
		||||
            errors.push(syn::Error::new_spanned(variadic, "... not allowed here"));
 | 
			
		||||
        }
 | 
			
		||||
        if !matches!(output, ReturnType::Default) {
 | 
			
		||||
            errors.push(syn::Error::new_spanned(
 | 
			
		||||
                output,
 | 
			
		||||
                "return type not allowed here",
 | 
			
		||||
            ));
 | 
			
		||||
        }
 | 
			
		||||
        let body_results = errors.ok(transform_body::transform_body(module_kind, block));
 | 
			
		||||
        errors.finish()?;
 | 
			
		||||
        let (block, io) = body_results.unwrap();
 | 
			
		||||
        Ok(Self {
 | 
			
		||||
            attrs,
 | 
			
		||||
            config_options,
 | 
			
		||||
            module_kind,
 | 
			
		||||
            vis,
 | 
			
		||||
            sig,
 | 
			
		||||
            block,
 | 
			
		||||
            io,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ModuleFn {
 | 
			
		||||
    pub(crate) fn generate(self) -> TokenStream {
 | 
			
		||||
        let Self {
 | 
			
		||||
            attrs,
 | 
			
		||||
            config_options,
 | 
			
		||||
            module_kind,
 | 
			
		||||
            vis,
 | 
			
		||||
            sig,
 | 
			
		||||
            block,
 | 
			
		||||
            io,
 | 
			
		||||
        } = self;
 | 
			
		||||
        let ConfigOptions {
 | 
			
		||||
            outline_generated: _,
 | 
			
		||||
            extern_: _,
 | 
			
		||||
        } = config_options.body;
 | 
			
		||||
        let mut outer_sig = sig.clone();
 | 
			
		||||
        let mut body_sig = sig;
 | 
			
		||||
        let param_names =
 | 
			
		||||
            Vec::from_iter(outer_sig.inputs.iter_mut().enumerate().map(|(index, arg)| {
 | 
			
		||||
                let FnArg::Typed(arg) = arg else {
 | 
			
		||||
                    unreachable!("already checked");
 | 
			
		||||
                };
 | 
			
		||||
                let name = if let syn::Pat::Ident(pat) = &*arg.pat {
 | 
			
		||||
                    pat.ident.clone()
 | 
			
		||||
                } else {
 | 
			
		||||
                    format_ident!("__param{}", index)
 | 
			
		||||
                };
 | 
			
		||||
                *arg.pat = syn::Pat::Ident(syn::PatIdent {
 | 
			
		||||
                    attrs: vec![],
 | 
			
		||||
                    by_ref: None,
 | 
			
		||||
                    mutability: None,
 | 
			
		||||
                    ident: name.clone(),
 | 
			
		||||
                    subpat: None,
 | 
			
		||||
                });
 | 
			
		||||
                name
 | 
			
		||||
            }));
 | 
			
		||||
        let module_kind_ty = match module_kind {
 | 
			
		||||
            ModuleKind::Extern => quote! { ::fayalite::module::ExternModule },
 | 
			
		||||
            ModuleKind::Normal => quote! { ::fayalite::module::NormalModule },
 | 
			
		||||
        };
 | 
			
		||||
        let fn_name = &outer_sig.ident;
 | 
			
		||||
        body_sig.ident = parse_quote! {__body};
 | 
			
		||||
        body_sig.inputs.insert(
 | 
			
		||||
            0,
 | 
			
		||||
            parse_quote! {m: &mut ::fayalite::module::ModuleBuilder<#fn_name, #module_kind_ty>},
 | 
			
		||||
        );
 | 
			
		||||
        let body_fn = ItemFn {
 | 
			
		||||
            attrs: vec![],
 | 
			
		||||
            vis: Visibility::Inherited,
 | 
			
		||||
            sig: body_sig,
 | 
			
		||||
            block,
 | 
			
		||||
        };
 | 
			
		||||
        outer_sig.output =
 | 
			
		||||
            parse_quote! {-> ::fayalite::intern::Interned<::fayalite::module::Module<#fn_name>>};
 | 
			
		||||
        let io_flips = io
 | 
			
		||||
            .iter()
 | 
			
		||||
            .map(|io| match io.kind.kind {
 | 
			
		||||
                ModuleIOKind::Input((input,)) => quote_spanned! {input.span=>
 | 
			
		||||
                    #[hdl(flip)]
 | 
			
		||||
                },
 | 
			
		||||
                ModuleIOKind::Output(_) => quote! {},
 | 
			
		||||
            })
 | 
			
		||||
            .collect::<Vec<_>>();
 | 
			
		||||
        let io_types = io.iter().map(|io| &io.kind.ty).collect::<Vec<_>>();
 | 
			
		||||
        let io_names = io.iter().map(|io| &io.name).collect::<Vec<_>>();
 | 
			
		||||
        let fn_name_str = fn_name.to_string();
 | 
			
		||||
        let block = parse_quote! {{
 | 
			
		||||
            #body_fn
 | 
			
		||||
            ::fayalite::module::ModuleBuilder::run(#fn_name_str, |m| __body(m, #(#param_names,)*))
 | 
			
		||||
        }};
 | 
			
		||||
        let fixed_type = io.iter().all(|io| io.kind.ty_expr.is_none());
 | 
			
		||||
        let struct_options = if fixed_type {
 | 
			
		||||
            quote! { #[hdl(fixed_type)] }
 | 
			
		||||
        } else {
 | 
			
		||||
            quote! {}
 | 
			
		||||
        };
 | 
			
		||||
        let the_struct: ItemStruct = parse_quote! {
 | 
			
		||||
            #[derive(::fayalite::__std::clone::Clone,
 | 
			
		||||
                ::fayalite::__std::hash::Hash,
 | 
			
		||||
                ::fayalite::__std::cmp::PartialEq,
 | 
			
		||||
                ::fayalite::__std::cmp::Eq,
 | 
			
		||||
                ::fayalite::__std::fmt::Debug)]
 | 
			
		||||
            #[allow(non_camel_case_types)]
 | 
			
		||||
            #struct_options
 | 
			
		||||
            #vis struct #fn_name {
 | 
			
		||||
                #(
 | 
			
		||||
                #io_flips
 | 
			
		||||
                #vis #io_names: #io_types,)*
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
        let mut struct_without_hdl_attrs = the_struct.clone();
 | 
			
		||||
        let mut struct_without_derives = the_struct;
 | 
			
		||||
        retain_struct_attrs(&mut struct_without_hdl_attrs, |attr| !is_hdl_attr(attr));
 | 
			
		||||
        retain_struct_attrs(&mut struct_without_derives, |attr| {
 | 
			
		||||
            !attr.path().is_ident("derive")
 | 
			
		||||
        });
 | 
			
		||||
        let outer_fn = ItemFn {
 | 
			
		||||
            attrs,
 | 
			
		||||
            vis,
 | 
			
		||||
            sig: outer_sig,
 | 
			
		||||
            block,
 | 
			
		||||
        };
 | 
			
		||||
        let mut retval = outer_fn.into_token_stream();
 | 
			
		||||
        struct_without_hdl_attrs.to_tokens(&mut retval);
 | 
			
		||||
        retval.extend(
 | 
			
		||||
            crate::value_derive_struct::value_derive_struct(struct_without_derives).unwrap(),
 | 
			
		||||
        );
 | 
			
		||||
        retval
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1566
									
								
								crates/fayalite-proc-macros-impl/src/module/transform_body.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1566
									
								
								crates/fayalite-proc-macros-impl/src/module/transform_body.rs
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							| 
						 | 
				
			
			@ -0,0 +1,530 @@
 | 
			
		|||
// SPDX-License-Identifier: LGPL-3.0-or-later
 | 
			
		||||
// See Notices.txt for copyright information
 | 
			
		||||
use crate::{module::transform_body::Visitor, options, Errors, HdlAttr};
 | 
			
		||||
use proc_macro2::{Span, TokenStream};
 | 
			
		||||
use quote::{format_ident, quote_spanned, ToTokens, TokenStreamExt};
 | 
			
		||||
use syn::{
 | 
			
		||||
    parse::Nothing,
 | 
			
		||||
    parse_quote, parse_quote_spanned,
 | 
			
		||||
    punctuated::{Pair, Punctuated},
 | 
			
		||||
    spanned::Spanned,
 | 
			
		||||
    token::{Brace, Paren},
 | 
			
		||||
    Attribute, Expr, ExprArray, ExprCall, ExprGroup, ExprPath, ExprStruct, ExprTuple, FieldValue,
 | 
			
		||||
    Ident, Index, Member, Path, PathArguments, PathSegment, Token, TypePath,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
options! {
 | 
			
		||||
    #[options = AggregateLiteralOptions]
 | 
			
		||||
    #[no_ident_fragment]
 | 
			
		||||
    pub(crate) enum AggregateLiteralOption {
 | 
			
		||||
        Struct(struct_),
 | 
			
		||||
        Enum(enum_),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug)]
 | 
			
		||||
pub(crate) struct StructOrEnumPath {
 | 
			
		||||
    pub(crate) ty: TypePath,
 | 
			
		||||
    pub(crate) variant: Option<(TypePath, Ident)>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Copy, Clone)]
 | 
			
		||||
pub(crate) struct SingleSegmentVariant {
 | 
			
		||||
    pub(crate) name: &'static str,
 | 
			
		||||
    pub(crate) make_type_path: fn(Span, &PathArguments) -> Path,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl StructOrEnumPath {
 | 
			
		||||
    pub(crate) const SINGLE_SEGMENT_VARIANTS: &'static [SingleSegmentVariant] = {
 | 
			
		||||
        fn make_option_type_path(span: Span, arguments: &PathArguments) -> Path {
 | 
			
		||||
            let arguments = if arguments.is_none() {
 | 
			
		||||
                quote_spanned! {span=>
 | 
			
		||||
                    <_>
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                arguments.to_token_stream()
 | 
			
		||||
            };
 | 
			
		||||
            parse_quote_spanned! {span=>
 | 
			
		||||
                ::fayalite::__std::option::Option #arguments
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        fn make_result_type_path(span: Span, arguments: &PathArguments) -> Path {
 | 
			
		||||
            let arguments = if arguments.is_none() {
 | 
			
		||||
                quote_spanned! {span=>
 | 
			
		||||
                    <_, _>
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                arguments.to_token_stream()
 | 
			
		||||
            };
 | 
			
		||||
            parse_quote_spanned! {span=>
 | 
			
		||||
                ::fayalite::__std::result::Result #arguments
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        &[
 | 
			
		||||
            SingleSegmentVariant {
 | 
			
		||||
                name: "Some",
 | 
			
		||||
                make_type_path: make_option_type_path,
 | 
			
		||||
            },
 | 
			
		||||
            SingleSegmentVariant {
 | 
			
		||||
                name: "None",
 | 
			
		||||
                make_type_path: make_option_type_path,
 | 
			
		||||
            },
 | 
			
		||||
            SingleSegmentVariant {
 | 
			
		||||
                name: "Ok",
 | 
			
		||||
                make_type_path: make_result_type_path,
 | 
			
		||||
            },
 | 
			
		||||
            SingleSegmentVariant {
 | 
			
		||||
                name: "Err",
 | 
			
		||||
                make_type_path: make_result_type_path,
 | 
			
		||||
            },
 | 
			
		||||
        ]
 | 
			
		||||
    };
 | 
			
		||||
    pub(crate) fn new(
 | 
			
		||||
        errors: &mut Errors,
 | 
			
		||||
        path: TypePath,
 | 
			
		||||
        options: &AggregateLiteralOptions,
 | 
			
		||||
    ) -> Result<Self, ()> {
 | 
			
		||||
        let Path {
 | 
			
		||||
            leading_colon,
 | 
			
		||||
            segments,
 | 
			
		||||
        } = &path.path;
 | 
			
		||||
        let qself_position = path.qself.as_ref().map(|qself| qself.position).unwrap_or(0);
 | 
			
		||||
        let variant_name = if qself_position < segments.len() {
 | 
			
		||||
            Some(segments.last().unwrap().ident.clone())
 | 
			
		||||
        } else {
 | 
			
		||||
            None
 | 
			
		||||
        };
 | 
			
		||||
        let enum_type = 'guess_enum_type: {
 | 
			
		||||
            if options.enum_.is_some() {
 | 
			
		||||
                if let Some((struct_,)) = options.struct_ {
 | 
			
		||||
                    errors.error(
 | 
			
		||||
                        struct_,
 | 
			
		||||
                        "can't specify both #[hdl(enum)] and #[hdl(struct)]",
 | 
			
		||||
                    );
 | 
			
		||||
                }
 | 
			
		||||
                break 'guess_enum_type Some(None);
 | 
			
		||||
            }
 | 
			
		||||
            if options.struct_.is_some() {
 | 
			
		||||
                break 'guess_enum_type None;
 | 
			
		||||
            }
 | 
			
		||||
            if path.qself.is_none() && leading_colon.is_none() && segments.len() == 1 {
 | 
			
		||||
                let PathSegment { ident, arguments } = &segments[0];
 | 
			
		||||
                for &SingleSegmentVariant {
 | 
			
		||||
                    name,
 | 
			
		||||
                    make_type_path,
 | 
			
		||||
                } in Self::SINGLE_SEGMENT_VARIANTS
 | 
			
		||||
                {
 | 
			
		||||
                    if ident == name {
 | 
			
		||||
                        break 'guess_enum_type Some(Some(TypePath {
 | 
			
		||||
                            qself: None,
 | 
			
		||||
                            path: make_type_path(ident.span(), arguments),
 | 
			
		||||
                        }));
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if segments.len() == qself_position + 2
 | 
			
		||||
                && segments[qself_position + 1].arguments.is_none()
 | 
			
		||||
                && (path.qself.is_some()
 | 
			
		||||
                    || segments[qself_position].ident.to_string().as_bytes()[0]
 | 
			
		||||
                        .is_ascii_uppercase())
 | 
			
		||||
            {
 | 
			
		||||
                let mut ty = path.clone();
 | 
			
		||||
                ty.path.segments.pop();
 | 
			
		||||
                ty.path.segments.pop_punct();
 | 
			
		||||
                break 'guess_enum_type Some(Some(ty));
 | 
			
		||||
            }
 | 
			
		||||
            None
 | 
			
		||||
        };
 | 
			
		||||
        if let Some(enum_type) = enum_type {
 | 
			
		||||
            let ty = if let Some(enum_type) = enum_type {
 | 
			
		||||
                enum_type
 | 
			
		||||
            } else {
 | 
			
		||||
                if qself_position >= segments.len() {
 | 
			
		||||
                    errors.error(path, "#[hdl]: can't figure out enum's type");
 | 
			
		||||
                    return Err(());
 | 
			
		||||
                }
 | 
			
		||||
                let mut ty = path.clone();
 | 
			
		||||
                ty.path.segments.pop();
 | 
			
		||||
                ty.path.segments.pop_punct();
 | 
			
		||||
                ty
 | 
			
		||||
            };
 | 
			
		||||
            let Some(variant_name) = variant_name else {
 | 
			
		||||
                errors.error(path, "#[hdl]: can't figure out enum's variant name");
 | 
			
		||||
                return Err(());
 | 
			
		||||
            };
 | 
			
		||||
            Ok(Self {
 | 
			
		||||
                ty,
 | 
			
		||||
                variant: Some((path, variant_name)),
 | 
			
		||||
            })
 | 
			
		||||
        } else {
 | 
			
		||||
            Ok(Self {
 | 
			
		||||
                ty: path,
 | 
			
		||||
                variant: None,
 | 
			
		||||
            })
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Copy, Clone, Debug)]
 | 
			
		||||
pub(crate) enum BraceOrParen {
 | 
			
		||||
    Brace(Brace),
 | 
			
		||||
    Paren(Paren),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl BraceOrParen {
 | 
			
		||||
    pub(crate) fn surround(self, tokens: &mut TokenStream, f: impl FnOnce(&mut TokenStream)) {
 | 
			
		||||
        match self {
 | 
			
		||||
            BraceOrParen::Brace(v) => v.surround(tokens, f),
 | 
			
		||||
            BraceOrParen::Paren(v) => v.surround(tokens, f),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
pub(crate) struct StructOrEnumLiteralField {
 | 
			
		||||
    pub(crate) attrs: Vec<Attribute>,
 | 
			
		||||
    pub(crate) member: Member,
 | 
			
		||||
    pub(crate) colon_token: Option<Token![:]>,
 | 
			
		||||
    pub(crate) expr: Expr,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
pub(crate) struct StructOrEnumLiteral {
 | 
			
		||||
    pub(crate) attrs: Vec<Attribute>,
 | 
			
		||||
    pub(crate) path: TypePath,
 | 
			
		||||
    pub(crate) brace_or_paren: BraceOrParen,
 | 
			
		||||
    pub(crate) fields: Punctuated<StructOrEnumLiteralField, Token![,]>,
 | 
			
		||||
    pub(crate) dot2_token: Option<Token![..]>,
 | 
			
		||||
    pub(crate) rest: Option<Box<Expr>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl StructOrEnumLiteral {
 | 
			
		||||
    pub(crate) fn map_field_exprs(self, mut f: impl FnMut(Expr) -> Expr) -> Self {
 | 
			
		||||
        self.map_fields(|mut field| {
 | 
			
		||||
            field.expr = f(field.expr);
 | 
			
		||||
            field
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
    pub(crate) fn map_fields(
 | 
			
		||||
        self,
 | 
			
		||||
        mut f: impl FnMut(StructOrEnumLiteralField) -> StructOrEnumLiteralField,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        let Self {
 | 
			
		||||
            attrs,
 | 
			
		||||
            path,
 | 
			
		||||
            brace_or_paren,
 | 
			
		||||
            fields,
 | 
			
		||||
            dot2_token,
 | 
			
		||||
            rest,
 | 
			
		||||
        } = self;
 | 
			
		||||
        let fields = Punctuated::from_iter(fields.into_pairs().map(|p| {
 | 
			
		||||
            let (field, comma) = p.into_tuple();
 | 
			
		||||
            Pair::new(f(field), comma)
 | 
			
		||||
        }));
 | 
			
		||||
        Self {
 | 
			
		||||
            attrs,
 | 
			
		||||
            path,
 | 
			
		||||
            brace_or_paren,
 | 
			
		||||
            fields,
 | 
			
		||||
            dot2_token,
 | 
			
		||||
            rest,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<ExprStruct> for StructOrEnumLiteral {
 | 
			
		||||
    fn from(value: ExprStruct) -> Self {
 | 
			
		||||
        let ExprStruct {
 | 
			
		||||
            attrs,
 | 
			
		||||
            qself,
 | 
			
		||||
            path,
 | 
			
		||||
            brace_token,
 | 
			
		||||
            fields,
 | 
			
		||||
            dot2_token,
 | 
			
		||||
            rest,
 | 
			
		||||
        } = value;
 | 
			
		||||
        Self {
 | 
			
		||||
            attrs,
 | 
			
		||||
            path: TypePath { qself, path },
 | 
			
		||||
            brace_or_paren: BraceOrParen::Brace(brace_token),
 | 
			
		||||
            fields: Punctuated::from_iter(fields.into_pairs().map(|v| {
 | 
			
		||||
                let (
 | 
			
		||||
                    FieldValue {
 | 
			
		||||
                        attrs,
 | 
			
		||||
                        member,
 | 
			
		||||
                        colon_token,
 | 
			
		||||
                        expr,
 | 
			
		||||
                    },
 | 
			
		||||
                    comma,
 | 
			
		||||
                ) = v.into_tuple();
 | 
			
		||||
                Pair::new(
 | 
			
		||||
                    StructOrEnumLiteralField {
 | 
			
		||||
                        attrs,
 | 
			
		||||
                        member,
 | 
			
		||||
                        colon_token,
 | 
			
		||||
                        expr,
 | 
			
		||||
                    },
 | 
			
		||||
                    comma,
 | 
			
		||||
                )
 | 
			
		||||
            })),
 | 
			
		||||
            dot2_token,
 | 
			
		||||
            rest,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn expr_to_member(expr: &Expr) -> Option<Member> {
 | 
			
		||||
    syn::parse2(expr.to_token_stream()).ok()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ToTokens for StructOrEnumLiteral {
 | 
			
		||||
    fn to_tokens(&self, tokens: &mut TokenStream) {
 | 
			
		||||
        let Self {
 | 
			
		||||
            attrs,
 | 
			
		||||
            path,
 | 
			
		||||
            brace_or_paren,
 | 
			
		||||
            fields,
 | 
			
		||||
            dot2_token,
 | 
			
		||||
            rest,
 | 
			
		||||
        } = self;
 | 
			
		||||
        tokens.append_all(attrs);
 | 
			
		||||
        path.to_tokens(tokens);
 | 
			
		||||
        brace_or_paren.surround(tokens, |tokens| {
 | 
			
		||||
            match brace_or_paren {
 | 
			
		||||
                BraceOrParen::Brace(_) => {
 | 
			
		||||
                    for (
 | 
			
		||||
                        StructOrEnumLiteralField {
 | 
			
		||||
                            attrs,
 | 
			
		||||
                            member,
 | 
			
		||||
                            mut colon_token,
 | 
			
		||||
                            expr,
 | 
			
		||||
                        },
 | 
			
		||||
                        comma,
 | 
			
		||||
                    ) in fields.pairs().map(|v| v.into_tuple())
 | 
			
		||||
                    {
 | 
			
		||||
                        tokens.append_all(attrs);
 | 
			
		||||
                        if Some(member) != expr_to_member(expr).as_ref() {
 | 
			
		||||
                            colon_token = Some(<Token![:]>::default());
 | 
			
		||||
                        }
 | 
			
		||||
                        member.to_tokens(tokens);
 | 
			
		||||
                        colon_token.to_tokens(tokens);
 | 
			
		||||
                        expr.to_tokens(tokens);
 | 
			
		||||
                        comma.to_tokens(tokens);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                BraceOrParen::Paren(_) => {
 | 
			
		||||
                    for (
 | 
			
		||||
                        StructOrEnumLiteralField {
 | 
			
		||||
                            attrs,
 | 
			
		||||
                            member: _,
 | 
			
		||||
                            colon_token: _,
 | 
			
		||||
                            expr,
 | 
			
		||||
                        },
 | 
			
		||||
                        comma,
 | 
			
		||||
                    ) in fields.pairs().map(|v| v.into_tuple())
 | 
			
		||||
                    {
 | 
			
		||||
                        tokens.append_all(attrs);
 | 
			
		||||
                        expr.to_tokens(tokens);
 | 
			
		||||
                        comma.to_tokens(tokens);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if let Some(rest) = rest {
 | 
			
		||||
                dot2_token.unwrap_or_default().to_tokens(tokens);
 | 
			
		||||
                rest.to_tokens(tokens);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Visitor {
 | 
			
		||||
    pub(crate) fn process_hdl_array(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        hdl_attr: HdlAttr<Nothing>,
 | 
			
		||||
        mut expr_array: ExprArray,
 | 
			
		||||
    ) -> Expr {
 | 
			
		||||
        self.require_normal_module(hdl_attr);
 | 
			
		||||
        for elem in &mut expr_array.elems {
 | 
			
		||||
            *elem = parse_quote_spanned! {elem.span()=>
 | 
			
		||||
                ::fayalite::expr::ToExpr::to_expr(&(#elem))
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
        parse_quote! {::fayalite::expr::ToExpr::to_expr(&#expr_array)}
 | 
			
		||||
    }
 | 
			
		||||
    pub(crate) fn process_struct_enum(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        hdl_attr: HdlAttr<AggregateLiteralOptions>,
 | 
			
		||||
        mut literal: StructOrEnumLiteral,
 | 
			
		||||
    ) -> Expr {
 | 
			
		||||
        let span = hdl_attr.hdl.span;
 | 
			
		||||
        if let Some(rest) = literal.rest.take() {
 | 
			
		||||
            self.errors
 | 
			
		||||
                .error(rest, "#[hdl] struct functional update syntax not supported");
 | 
			
		||||
        }
 | 
			
		||||
        let mut next_var = 0usize;
 | 
			
		||||
        let mut new_var = || -> Ident {
 | 
			
		||||
            let retval = format_ident!("__v{}", next_var, span = span);
 | 
			
		||||
            next_var += 1;
 | 
			
		||||
            retval
 | 
			
		||||
        };
 | 
			
		||||
        let infallible_var = new_var();
 | 
			
		||||
        let retval_var = new_var();
 | 
			
		||||
        let mut lets = vec![];
 | 
			
		||||
        let mut build_steps = vec![];
 | 
			
		||||
        let literal = literal.map_field_exprs(|expr| {
 | 
			
		||||
            let field_var = new_var();
 | 
			
		||||
            lets.push(quote_spanned! {span=>
 | 
			
		||||
                let #field_var = ::fayalite::expr::ToExpr::to_expr(&#expr);
 | 
			
		||||
            });
 | 
			
		||||
            parse_quote! { #field_var }
 | 
			
		||||
        });
 | 
			
		||||
        let Ok(StructOrEnumPath { ty, variant }) =
 | 
			
		||||
            StructOrEnumPath::new(&mut self.errors, literal.path.clone(), &hdl_attr.body)
 | 
			
		||||
        else {
 | 
			
		||||
            return parse_quote_spanned! {span=>
 | 
			
		||||
                {}
 | 
			
		||||
            };
 | 
			
		||||
        };
 | 
			
		||||
        for StructOrEnumLiteralField {
 | 
			
		||||
            attrs: _,
 | 
			
		||||
            member,
 | 
			
		||||
            colon_token: _,
 | 
			
		||||
            expr,
 | 
			
		||||
        } in literal.fields.iter()
 | 
			
		||||
        {
 | 
			
		||||
            let field_fn = format_ident!("field_{}", member);
 | 
			
		||||
            build_steps.push(quote_spanned! {span=>
 | 
			
		||||
                let #retval_var = #retval_var.#field_fn(#expr);
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        let check_literal = literal.map_field_exprs(|expr| {
 | 
			
		||||
            parse_quote_spanned! {span=>
 | 
			
		||||
                ::fayalite::expr::value_from_expr_type(#expr, #infallible_var)
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        let make_expr_fn = if let Some((_variant_path, variant_ident)) = &variant {
 | 
			
		||||
            let variant_fn = format_ident!("variant_{}", variant_ident);
 | 
			
		||||
            build_steps.push(quote_spanned! {span=>
 | 
			
		||||
                let #retval_var = #retval_var.#variant_fn();
 | 
			
		||||
            });
 | 
			
		||||
            quote_spanned! {span=>
 | 
			
		||||
                ::fayalite::expr::make_enum_expr
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            build_steps.push(quote_spanned! {span=>
 | 
			
		||||
                let #retval_var = #retval_var.build();
 | 
			
		||||
            });
 | 
			
		||||
            quote_spanned! {span=>
 | 
			
		||||
                ::fayalite::expr::make_bundle_expr
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
        let variant_or_type =
 | 
			
		||||
            variant.map_or_else(|| ty.clone(), |(variant_path, _variant_ident)| variant_path);
 | 
			
		||||
        parse_quote_spanned! {span=>
 | 
			
		||||
            {
 | 
			
		||||
                #(#lets)*
 | 
			
		||||
                #make_expr_fn::<#ty>(|#infallible_var| {
 | 
			
		||||
                    let #retval_var = #check_literal;
 | 
			
		||||
                    match #retval_var {
 | 
			
		||||
                        #variant_or_type { .. } => #retval_var,
 | 
			
		||||
                        #[allow(unreachable_patterns)]
 | 
			
		||||
                        _ => match #infallible_var {},
 | 
			
		||||
                    }
 | 
			
		||||
                }, |#retval_var| {
 | 
			
		||||
                    #(#build_steps)*
 | 
			
		||||
                    #retval_var
 | 
			
		||||
                })
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub(crate) fn process_hdl_struct(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        hdl_attr: HdlAttr<AggregateLiteralOptions>,
 | 
			
		||||
        expr_struct: ExprStruct,
 | 
			
		||||
    ) -> Expr {
 | 
			
		||||
        self.require_normal_module(&hdl_attr);
 | 
			
		||||
        self.process_struct_enum(hdl_attr, expr_struct.into())
 | 
			
		||||
    }
 | 
			
		||||
    pub(crate) fn process_hdl_tuple(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        hdl_attr: HdlAttr<Nothing>,
 | 
			
		||||
        expr_tuple: ExprTuple,
 | 
			
		||||
    ) -> Expr {
 | 
			
		||||
        self.require_normal_module(hdl_attr);
 | 
			
		||||
        parse_quote_spanned! {expr_tuple.span()=>
 | 
			
		||||
            ::fayalite::expr::ToExpr::to_expr(&#expr_tuple)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub(crate) fn process_hdl_path(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        hdl_attr: HdlAttr<Nothing>,
 | 
			
		||||
        expr_path: ExprPath,
 | 
			
		||||
    ) -> Expr {
 | 
			
		||||
        self.require_normal_module(hdl_attr);
 | 
			
		||||
        parse_quote_spanned! {expr_path.span()=>
 | 
			
		||||
            ::fayalite::expr::ToExpr::to_expr(&#expr_path)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub(crate) fn process_hdl_call(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        hdl_attr: HdlAttr<AggregateLiteralOptions>,
 | 
			
		||||
        expr_call: ExprCall,
 | 
			
		||||
    ) -> Expr {
 | 
			
		||||
        self.require_normal_module(&hdl_attr);
 | 
			
		||||
        let ExprCall {
 | 
			
		||||
            attrs: mut literal_attrs,
 | 
			
		||||
            func,
 | 
			
		||||
            paren_token,
 | 
			
		||||
            args,
 | 
			
		||||
        } = expr_call;
 | 
			
		||||
        let mut path_expr = *func;
 | 
			
		||||
        let path = loop {
 | 
			
		||||
            break match path_expr {
 | 
			
		||||
                Expr::Group(ExprGroup {
 | 
			
		||||
                    attrs,
 | 
			
		||||
                    group_token: _,
 | 
			
		||||
                    expr,
 | 
			
		||||
                }) => {
 | 
			
		||||
                    literal_attrs.extend(attrs);
 | 
			
		||||
                    path_expr = *expr;
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
                Expr::Path(ExprPath { attrs, qself, path }) => {
 | 
			
		||||
                    literal_attrs.extend(attrs);
 | 
			
		||||
                    TypePath { qself, path }
 | 
			
		||||
                }
 | 
			
		||||
                _ => {
 | 
			
		||||
                    self.errors.error(&path_expr, "missing tuple struct's name");
 | 
			
		||||
                    return parse_quote_spanned! {path_expr.span()=>
 | 
			
		||||
                        {}
 | 
			
		||||
                    };
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
        };
 | 
			
		||||
        let fields = Punctuated::from_iter(args.into_pairs().enumerate().map(|(index, p)| {
 | 
			
		||||
            let (expr, comma) = p.into_tuple();
 | 
			
		||||
            let mut index = Index::from(index);
 | 
			
		||||
            index.span = hdl_attr.hdl.span;
 | 
			
		||||
            Pair::new(
 | 
			
		||||
                StructOrEnumLiteralField {
 | 
			
		||||
                    attrs: vec![],
 | 
			
		||||
                    member: Member::Unnamed(index),
 | 
			
		||||
                    colon_token: None,
 | 
			
		||||
                    expr,
 | 
			
		||||
                },
 | 
			
		||||
                comma,
 | 
			
		||||
            )
 | 
			
		||||
        }));
 | 
			
		||||
        self.process_struct_enum(
 | 
			
		||||
            hdl_attr,
 | 
			
		||||
            StructOrEnumLiteral {
 | 
			
		||||
                attrs: literal_attrs,
 | 
			
		||||
                path,
 | 
			
		||||
                brace_or_paren: BraceOrParen::Paren(paren_token),
 | 
			
		||||
                fields,
 | 
			
		||||
                dot2_token: None,
 | 
			
		||||
                rest: None,
 | 
			
		||||
            },
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,625 @@
 | 
			
		|||
// SPDX-License-Identifier: LGPL-3.0-or-later
 | 
			
		||||
// See Notices.txt for copyright information
 | 
			
		||||
use crate::{
 | 
			
		||||
    fold::impl_fold,
 | 
			
		||||
    module::transform_body::{
 | 
			
		||||
        expand_aggregate_literals::{AggregateLiteralOptions, StructOrEnumPath},
 | 
			
		||||
        with_debug_clone_and_fold, Visitor,
 | 
			
		||||
    },
 | 
			
		||||
    Errors, HdlAttr,
 | 
			
		||||
};
 | 
			
		||||
use proc_macro2::{Span, TokenStream};
 | 
			
		||||
use quote::{ToTokens, TokenStreamExt};
 | 
			
		||||
use syn::{
 | 
			
		||||
    fold::{fold_arm, fold_expr_match, fold_pat, Fold},
 | 
			
		||||
    parse::Nothing,
 | 
			
		||||
    parse_quote_spanned,
 | 
			
		||||
    punctuated::{Pair, Punctuated},
 | 
			
		||||
    spanned::Spanned,
 | 
			
		||||
    token::{Brace, Paren},
 | 
			
		||||
    Arm, Attribute, Expr, ExprMatch, FieldPat, Ident, Index, Member, Pat, PatIdent, PatOr,
 | 
			
		||||
    PatParen, PatPath, PatRest, PatStruct, PatTupleStruct, PatWild, Path, Token, TypePath,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
with_debug_clone_and_fold! {
 | 
			
		||||
    struct MatchPatBinding<> {
 | 
			
		||||
        ident: Ident,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ToTokens for MatchPatBinding {
 | 
			
		||||
    fn to_tokens(&self, tokens: &mut TokenStream) {
 | 
			
		||||
        let Self { ident } = self;
 | 
			
		||||
        ident.to_tokens(tokens);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
with_debug_clone_and_fold! {
 | 
			
		||||
    struct MatchPatParen<P> {
 | 
			
		||||
        paren_token: Paren,
 | 
			
		||||
        pat: Box<P>,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<P: ToTokens> ToTokens for MatchPatParen<P> {
 | 
			
		||||
    fn to_tokens(&self, tokens: &mut TokenStream) {
 | 
			
		||||
        let Self { paren_token, pat } = self;
 | 
			
		||||
        paren_token.surround(tokens, |tokens| pat.to_tokens(tokens));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
with_debug_clone_and_fold! {
 | 
			
		||||
    struct MatchPatOr<P> {
 | 
			
		||||
        leading_vert: Option<Token![|]>,
 | 
			
		||||
        cases: Punctuated<P, Token![|]>,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<P: ToTokens> ToTokens for MatchPatOr<P> {
 | 
			
		||||
    fn to_tokens(&self, tokens: &mut TokenStream) {
 | 
			
		||||
        let Self {
 | 
			
		||||
            leading_vert,
 | 
			
		||||
            cases,
 | 
			
		||||
        } = self;
 | 
			
		||||
        leading_vert.to_tokens(tokens);
 | 
			
		||||
        cases.to_tokens(tokens);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
with_debug_clone_and_fold! {
 | 
			
		||||
    struct MatchPatWild<> {
 | 
			
		||||
        underscore_token: Token![_],
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ToTokens for MatchPatWild {
 | 
			
		||||
    fn to_tokens(&self, tokens: &mut TokenStream) {
 | 
			
		||||
        let Self { underscore_token } = self;
 | 
			
		||||
        underscore_token.to_tokens(tokens);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
with_debug_clone_and_fold! {
 | 
			
		||||
    struct MatchPatStructField<> {
 | 
			
		||||
        member: Member,
 | 
			
		||||
        colon_token: Option<Token![:]>,
 | 
			
		||||
        pat: MatchPatSimple,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ToTokens for MatchPatStructField {
 | 
			
		||||
    fn to_tokens(&self, tokens: &mut TokenStream) {
 | 
			
		||||
        let Self {
 | 
			
		||||
            member,
 | 
			
		||||
            colon_token,
 | 
			
		||||
            pat,
 | 
			
		||||
        } = self;
 | 
			
		||||
        member.to_tokens(tokens);
 | 
			
		||||
        colon_token.to_tokens(tokens);
 | 
			
		||||
        pat.to_tokens(tokens);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl MatchPatStructField {
 | 
			
		||||
    fn parse(state: &mut HdlMatchParseState<'_>, field_pat: FieldPat) -> Result<Self, ()> {
 | 
			
		||||
        let FieldPat {
 | 
			
		||||
            attrs: _,
 | 
			
		||||
            member,
 | 
			
		||||
            colon_token,
 | 
			
		||||
            pat,
 | 
			
		||||
        } = field_pat;
 | 
			
		||||
        Ok(Self {
 | 
			
		||||
            member,
 | 
			
		||||
            colon_token,
 | 
			
		||||
            pat: MatchPatSimple::parse(state, *pat)?,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
with_debug_clone_and_fold! {
 | 
			
		||||
    struct MatchPatStruct<> {
 | 
			
		||||
        resolved_path: Path,
 | 
			
		||||
        brace_token: Brace,
 | 
			
		||||
        fields: Punctuated<MatchPatStructField, Token![,]>,
 | 
			
		||||
        rest: Option<Token![..]>,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ToTokens for MatchPatStruct {
 | 
			
		||||
    fn to_tokens(&self, tokens: &mut TokenStream) {
 | 
			
		||||
        let Self {
 | 
			
		||||
            resolved_path,
 | 
			
		||||
            brace_token,
 | 
			
		||||
            fields,
 | 
			
		||||
            rest,
 | 
			
		||||
        } = self;
 | 
			
		||||
        resolved_path.to_tokens(tokens);
 | 
			
		||||
        brace_token.surround(tokens, |tokens| {
 | 
			
		||||
            fields.to_tokens(tokens);
 | 
			
		||||
            rest.to_tokens(tokens);
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
enum MatchPatSimple {
 | 
			
		||||
    Paren(MatchPatParen<MatchPatSimple>),
 | 
			
		||||
    Or(MatchPatOr<MatchPatSimple>),
 | 
			
		||||
    Binding(MatchPatBinding),
 | 
			
		||||
    Wild(MatchPatWild),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl_fold! {
 | 
			
		||||
    enum MatchPatSimple<> {
 | 
			
		||||
        Paren(MatchPatParen<MatchPatSimple>),
 | 
			
		||||
        Or(MatchPatOr<MatchPatSimple>),
 | 
			
		||||
        Binding(MatchPatBinding),
 | 
			
		||||
        Wild(MatchPatWild),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ToTokens for MatchPatSimple {
 | 
			
		||||
    fn to_tokens(&self, tokens: &mut TokenStream) {
 | 
			
		||||
        match self {
 | 
			
		||||
            Self::Or(v) => v.to_tokens(tokens),
 | 
			
		||||
            Self::Paren(v) => v.to_tokens(tokens),
 | 
			
		||||
            Self::Binding(v) => v.to_tokens(tokens),
 | 
			
		||||
            Self::Wild(v) => v.to_tokens(tokens),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn is_pat_ident_a_struct_or_enum_name(ident: &Ident) -> bool {
 | 
			
		||||
    ident
 | 
			
		||||
        .to_string()
 | 
			
		||||
        .starts_with(|ch: char| ch.is_ascii_uppercase())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
trait ParseMatchPat: Sized {
 | 
			
		||||
    fn simple(v: MatchPatSimple) -> Self;
 | 
			
		||||
    fn or(v: MatchPatOr<Self>) -> Self;
 | 
			
		||||
    fn paren(v: MatchPatParen<Self>) -> Self;
 | 
			
		||||
    fn struct_(
 | 
			
		||||
        state: &mut HdlMatchParseState<'_>,
 | 
			
		||||
        v: MatchPatStruct,
 | 
			
		||||
        struct_error_spanned: &dyn ToTokens,
 | 
			
		||||
    ) -> Result<Self, ()>;
 | 
			
		||||
    fn parse(state: &mut HdlMatchParseState<'_>, pat: Pat) -> Result<Self, ()> {
 | 
			
		||||
        match pat {
 | 
			
		||||
            Pat::Ident(PatIdent {
 | 
			
		||||
                attrs: _,
 | 
			
		||||
                by_ref,
 | 
			
		||||
                mutability,
 | 
			
		||||
                ident,
 | 
			
		||||
                subpat,
 | 
			
		||||
            }) => {
 | 
			
		||||
                if let Some(by_ref) = by_ref {
 | 
			
		||||
                    state
 | 
			
		||||
                        .errors
 | 
			
		||||
                        .error(by_ref, "ref not allowed in #[hdl] patterns");
 | 
			
		||||
                }
 | 
			
		||||
                if let Some(mutability) = mutability {
 | 
			
		||||
                    state
 | 
			
		||||
                        .errors
 | 
			
		||||
                        .error(mutability, "mut not allowed in #[hdl] patterns");
 | 
			
		||||
                }
 | 
			
		||||
                if let Some((at_token, _)) = subpat {
 | 
			
		||||
                    state
 | 
			
		||||
                        .errors
 | 
			
		||||
                        .error(at_token, "@ not allowed in #[hdl] patterns");
 | 
			
		||||
                }
 | 
			
		||||
                if is_pat_ident_a_struct_or_enum_name(&ident) {
 | 
			
		||||
                    let ident_span = ident.span();
 | 
			
		||||
                    let resolved_path = state.resolve_enum_struct_path(TypePath {
 | 
			
		||||
                        qself: None,
 | 
			
		||||
                        path: ident.clone().into(),
 | 
			
		||||
                    })?;
 | 
			
		||||
                    Self::struct_(
 | 
			
		||||
                        state,
 | 
			
		||||
                        MatchPatStruct {
 | 
			
		||||
                            resolved_path,
 | 
			
		||||
                            brace_token: Brace(ident_span),
 | 
			
		||||
                            fields: Punctuated::new(),
 | 
			
		||||
                            rest: None,
 | 
			
		||||
                        },
 | 
			
		||||
                        &ident,
 | 
			
		||||
                    )
 | 
			
		||||
                } else {
 | 
			
		||||
                    Ok(Self::simple(MatchPatSimple::Binding(MatchPatBinding {
 | 
			
		||||
                        ident,
 | 
			
		||||
                    })))
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            Pat::Or(PatOr {
 | 
			
		||||
                attrs: _,
 | 
			
		||||
                leading_vert,
 | 
			
		||||
                cases,
 | 
			
		||||
            }) => Ok(Self::or(MatchPatOr {
 | 
			
		||||
                leading_vert,
 | 
			
		||||
                cases: cases
 | 
			
		||||
                    .into_pairs()
 | 
			
		||||
                    .filter_map(|pair| {
 | 
			
		||||
                        let (pat, punct) = pair.into_tuple();
 | 
			
		||||
                        let pat = Self::parse(state, pat).ok()?;
 | 
			
		||||
                        Some(Pair::new(pat, punct))
 | 
			
		||||
                    })
 | 
			
		||||
                    .collect(),
 | 
			
		||||
            })),
 | 
			
		||||
            Pat::Paren(PatParen {
 | 
			
		||||
                attrs: _,
 | 
			
		||||
                paren_token,
 | 
			
		||||
                pat,
 | 
			
		||||
            }) => Ok(Self::paren(MatchPatParen {
 | 
			
		||||
                paren_token,
 | 
			
		||||
                pat: Box::new(Self::parse(state, *pat)?),
 | 
			
		||||
            })),
 | 
			
		||||
            Pat::Path(PatPath {
 | 
			
		||||
                attrs: _,
 | 
			
		||||
                qself,
 | 
			
		||||
                path,
 | 
			
		||||
            }) => {
 | 
			
		||||
                let path = TypePath { qself, path };
 | 
			
		||||
                let path_span = path.span();
 | 
			
		||||
                let resolved_path = state.resolve_enum_struct_path(path.clone())?;
 | 
			
		||||
                Self::struct_(
 | 
			
		||||
                    state,
 | 
			
		||||
                    MatchPatStruct {
 | 
			
		||||
                        resolved_path,
 | 
			
		||||
                        brace_token: Brace(path_span),
 | 
			
		||||
                        fields: Punctuated::new(),
 | 
			
		||||
                        rest: None,
 | 
			
		||||
                    },
 | 
			
		||||
                    &path,
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
            Pat::Struct(PatStruct {
 | 
			
		||||
                attrs: _,
 | 
			
		||||
                qself,
 | 
			
		||||
                path,
 | 
			
		||||
                brace_token,
 | 
			
		||||
                fields,
 | 
			
		||||
                rest,
 | 
			
		||||
            }) => {
 | 
			
		||||
                let fields = fields
 | 
			
		||||
                    .into_pairs()
 | 
			
		||||
                    .filter_map(|pair| {
 | 
			
		||||
                        let (field_pat, punct) = pair.into_tuple();
 | 
			
		||||
                        let field_pat = MatchPatStructField::parse(state, field_pat).ok()?;
 | 
			
		||||
                        Some(Pair::new(field_pat, punct))
 | 
			
		||||
                    })
 | 
			
		||||
                    .collect();
 | 
			
		||||
                let path = TypePath { qself, path };
 | 
			
		||||
                let resolved_path = state.resolve_enum_struct_path(path.clone())?;
 | 
			
		||||
                Self::struct_(
 | 
			
		||||
                    state,
 | 
			
		||||
                    MatchPatStruct {
 | 
			
		||||
                        resolved_path,
 | 
			
		||||
                        brace_token,
 | 
			
		||||
                        fields,
 | 
			
		||||
                        rest: rest.map(
 | 
			
		||||
                            |PatRest {
 | 
			
		||||
                                 attrs: _,
 | 
			
		||||
                                 dot2_token,
 | 
			
		||||
                             }| dot2_token,
 | 
			
		||||
                        ),
 | 
			
		||||
                    },
 | 
			
		||||
                    &path,
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
            Pat::TupleStruct(PatTupleStruct {
 | 
			
		||||
                attrs: _,
 | 
			
		||||
                qself,
 | 
			
		||||
                path,
 | 
			
		||||
                paren_token,
 | 
			
		||||
                mut elems,
 | 
			
		||||
            }) => {
 | 
			
		||||
                let rest = if let Some(&Pat::Rest(PatRest {
 | 
			
		||||
                    attrs: _,
 | 
			
		||||
                    dot2_token,
 | 
			
		||||
                })) = elems.last()
 | 
			
		||||
                {
 | 
			
		||||
                    elems.pop();
 | 
			
		||||
                    Some(dot2_token)
 | 
			
		||||
                } else {
 | 
			
		||||
                    None
 | 
			
		||||
                };
 | 
			
		||||
                let fields = elems
 | 
			
		||||
                    .into_pairs()
 | 
			
		||||
                    .enumerate()
 | 
			
		||||
                    .filter_map(|(index, pair)| {
 | 
			
		||||
                        let (pat, punct) = pair.into_tuple();
 | 
			
		||||
                        let pat = MatchPatSimple::parse(state, pat).ok()?;
 | 
			
		||||
                        let mut index = Index::from(index);
 | 
			
		||||
                        index.span = state.span;
 | 
			
		||||
                        let field = MatchPatStructField {
 | 
			
		||||
                            member: index.into(),
 | 
			
		||||
                            colon_token: Some(Token),
 | 
			
		||||
                            pat,
 | 
			
		||||
                        };
 | 
			
		||||
                        Some(Pair::new(field, punct))
 | 
			
		||||
                    })
 | 
			
		||||
                    .collect();
 | 
			
		||||
                let path = TypePath { qself, path };
 | 
			
		||||
                let resolved_path = state.resolve_enum_struct_path(path.clone())?;
 | 
			
		||||
                Self::struct_(
 | 
			
		||||
                    state,
 | 
			
		||||
                    MatchPatStruct {
 | 
			
		||||
                        resolved_path,
 | 
			
		||||
                        brace_token: Brace {
 | 
			
		||||
                            span: paren_token.span,
 | 
			
		||||
                        },
 | 
			
		||||
                        fields,
 | 
			
		||||
                        rest,
 | 
			
		||||
                    },
 | 
			
		||||
                    &path,
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
            Pat::Rest(_) => {
 | 
			
		||||
                state
 | 
			
		||||
                    .errors
 | 
			
		||||
                    .error(pat, "not allowed here in #[hdl] patterns");
 | 
			
		||||
                Err(())
 | 
			
		||||
            }
 | 
			
		||||
            Pat::Wild(PatWild {
 | 
			
		||||
                attrs: _,
 | 
			
		||||
                underscore_token,
 | 
			
		||||
            }) => Ok(Self::simple(MatchPatSimple::Wild(MatchPatWild {
 | 
			
		||||
                underscore_token,
 | 
			
		||||
            }))),
 | 
			
		||||
            Pat::Tuple(_) | Pat::Slice(_) | Pat::Const(_) | Pat::Lit(_) | Pat::Range(_) => {
 | 
			
		||||
                state
 | 
			
		||||
                    .errors
 | 
			
		||||
                    .error(pat, "not yet implemented in #[hdl] patterns");
 | 
			
		||||
                Err(())
 | 
			
		||||
            }
 | 
			
		||||
            _ => {
 | 
			
		||||
                state.errors.error(pat, "not allowed in #[hdl] patterns");
 | 
			
		||||
                Err(())
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ParseMatchPat for MatchPatSimple {
 | 
			
		||||
    fn simple(v: MatchPatSimple) -> Self {
 | 
			
		||||
        v
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn or(v: MatchPatOr<Self>) -> Self {
 | 
			
		||||
        Self::Or(v)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn paren(v: MatchPatParen<Self>) -> Self {
 | 
			
		||||
        Self::Paren(v)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn struct_(
 | 
			
		||||
        state: &mut HdlMatchParseState<'_>,
 | 
			
		||||
        _v: MatchPatStruct,
 | 
			
		||||
        struct_error_spanned: &dyn ToTokens,
 | 
			
		||||
    ) -> Result<Self, ()> {
 | 
			
		||||
        state.errors.error(
 | 
			
		||||
            struct_error_spanned,
 | 
			
		||||
            "not yet implemented inside structs/enums in #[hdl] patterns",
 | 
			
		||||
        );
 | 
			
		||||
        Err(())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
enum MatchPat {
 | 
			
		||||
    Simple(MatchPatSimple),
 | 
			
		||||
    Or(MatchPatOr<MatchPat>),
 | 
			
		||||
    Paren(MatchPatParen<MatchPat>),
 | 
			
		||||
    Struct(MatchPatStruct),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl_fold! {
 | 
			
		||||
    enum MatchPat<> {
 | 
			
		||||
        Simple(MatchPatSimple),
 | 
			
		||||
        Or(MatchPatOr<MatchPat>),
 | 
			
		||||
        Paren(MatchPatParen<MatchPat>),
 | 
			
		||||
        Struct(MatchPatStruct),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ParseMatchPat for MatchPat {
 | 
			
		||||
    fn simple(v: MatchPatSimple) -> Self {
 | 
			
		||||
        Self::Simple(v)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn or(v: MatchPatOr<Self>) -> Self {
 | 
			
		||||
        Self::Or(v)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn paren(v: MatchPatParen<Self>) -> Self {
 | 
			
		||||
        Self::Paren(v)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn struct_(
 | 
			
		||||
        _state: &mut HdlMatchParseState<'_>,
 | 
			
		||||
        v: MatchPatStruct,
 | 
			
		||||
        _struct_error_spanned: &dyn ToTokens,
 | 
			
		||||
    ) -> Result<Self, ()> {
 | 
			
		||||
        Ok(Self::Struct(v))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ToTokens for MatchPat {
 | 
			
		||||
    fn to_tokens(&self, tokens: &mut TokenStream) {
 | 
			
		||||
        match self {
 | 
			
		||||
            Self::Simple(v) => v.to_tokens(tokens),
 | 
			
		||||
            Self::Or(v) => v.to_tokens(tokens),
 | 
			
		||||
            Self::Paren(v) => v.to_tokens(tokens),
 | 
			
		||||
            Self::Struct(v) => v.to_tokens(tokens),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
with_debug_clone_and_fold! {
 | 
			
		||||
    struct MatchArm<> {
 | 
			
		||||
        attrs: Vec<Attribute>,
 | 
			
		||||
        pat: MatchPat,
 | 
			
		||||
        fat_arrow_token: Token![=>],
 | 
			
		||||
        body: Box<Expr>,
 | 
			
		||||
        comma: Option<Token![,]>,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl MatchArm {
 | 
			
		||||
    fn parse(state: &mut HdlMatchParseState<'_>, arm: Arm) -> Result<Self, ()> {
 | 
			
		||||
        let Arm {
 | 
			
		||||
            attrs,
 | 
			
		||||
            pat,
 | 
			
		||||
            guard,
 | 
			
		||||
            fat_arrow_token,
 | 
			
		||||
            body,
 | 
			
		||||
            comma,
 | 
			
		||||
        } = arm;
 | 
			
		||||
        if let Some((if_, _)) = guard {
 | 
			
		||||
            state
 | 
			
		||||
                .errors
 | 
			
		||||
                .error(if_, "#[hdl] match arm if clauses are not implemented");
 | 
			
		||||
        }
 | 
			
		||||
        Ok(Self {
 | 
			
		||||
            attrs,
 | 
			
		||||
            pat: MatchPat::parse(state, pat)?,
 | 
			
		||||
            fat_arrow_token,
 | 
			
		||||
            body,
 | 
			
		||||
            comma,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ToTokens for MatchArm {
 | 
			
		||||
    fn to_tokens(&self, tokens: &mut TokenStream) {
 | 
			
		||||
        let Self {
 | 
			
		||||
            attrs,
 | 
			
		||||
            pat,
 | 
			
		||||
            fat_arrow_token,
 | 
			
		||||
            body,
 | 
			
		||||
            comma,
 | 
			
		||||
        } = self;
 | 
			
		||||
        tokens.append_all(attrs);
 | 
			
		||||
        pat.to_tokens(tokens);
 | 
			
		||||
        fat_arrow_token.to_tokens(tokens);
 | 
			
		||||
        body.to_tokens(tokens);
 | 
			
		||||
        comma.to_tokens(tokens);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct RewriteAsCheckMatch {
 | 
			
		||||
    span: Span,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Fold for RewriteAsCheckMatch {
 | 
			
		||||
    fn fold_field_pat(&mut self, mut i: FieldPat) -> FieldPat {
 | 
			
		||||
        i.colon_token = Some(Token));
 | 
			
		||||
        i
 | 
			
		||||
    }
 | 
			
		||||
    fn fold_pat(&mut self, i: Pat) -> Pat {
 | 
			
		||||
        match i {
 | 
			
		||||
            Pat::Ident(PatIdent {
 | 
			
		||||
                attrs,
 | 
			
		||||
                by_ref,
 | 
			
		||||
                mutability,
 | 
			
		||||
                ident,
 | 
			
		||||
                subpat: None,
 | 
			
		||||
            }) if is_pat_ident_a_struct_or_enum_name(&ident) => {
 | 
			
		||||
                parse_quote_spanned! {ident.span()=>
 | 
			
		||||
                    #(#attrs)*
 | 
			
		||||
                    #by_ref
 | 
			
		||||
                    #mutability
 | 
			
		||||
                    #ident {}
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            _ => fold_pat(self, i),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    fn fold_pat_ident(&mut self, mut i: PatIdent) -> PatIdent {
 | 
			
		||||
        i.by_ref = Some(Token));
 | 
			
		||||
        i.mutability = None;
 | 
			
		||||
        i
 | 
			
		||||
    }
 | 
			
		||||
    fn fold_arm(&mut self, mut i: Arm) -> Arm {
 | 
			
		||||
        i.body = parse_quote_spanned! {self.span=>
 | 
			
		||||
            match __infallible {}
 | 
			
		||||
        };
 | 
			
		||||
        i.comma.get_or_insert_with(|| Token);
 | 
			
		||||
        fold_arm(self, i)
 | 
			
		||||
    }
 | 
			
		||||
    fn fold_expr_match(&mut self, mut i: ExprMatch) -> ExprMatch {
 | 
			
		||||
        i.expr = parse_quote_spanned! {self.span=>
 | 
			
		||||
            __match_value
 | 
			
		||||
        };
 | 
			
		||||
        fold_expr_match(self, i)
 | 
			
		||||
    }
 | 
			
		||||
    fn fold_expr(&mut self, i: Expr) -> Expr {
 | 
			
		||||
        // don't recurse into expressions
 | 
			
		||||
        i
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct HdlMatchParseState<'a> {
 | 
			
		||||
    errors: &'a mut Errors,
 | 
			
		||||
    span: Span,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl HdlMatchParseState<'_> {
 | 
			
		||||
    fn resolve_enum_struct_path(&mut self, path: TypePath) -> Result<Path, ()> {
 | 
			
		||||
        let StructOrEnumPath { ty, variant } =
 | 
			
		||||
            StructOrEnumPath::new(self.errors, path, &AggregateLiteralOptions::default())?;
 | 
			
		||||
        Ok(if let Some((_variant_path, variant_name)) = variant {
 | 
			
		||||
            parse_quote_spanned! {self.span=>
 | 
			
		||||
                __MatchTy::<#ty>::#variant_name
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            parse_quote_spanned! {self.span=>
 | 
			
		||||
                __MatchTy::<#ty>
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Visitor {
 | 
			
		||||
    pub(crate) fn process_hdl_match(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        _hdl_attr: HdlAttr<Nothing>,
 | 
			
		||||
        expr_match: ExprMatch,
 | 
			
		||||
    ) -> Expr {
 | 
			
		||||
        let span = expr_match.match_token.span();
 | 
			
		||||
        let check_match = RewriteAsCheckMatch { span }.fold_expr_match(expr_match.clone());
 | 
			
		||||
        let ExprMatch {
 | 
			
		||||
            attrs: _,
 | 
			
		||||
            match_token,
 | 
			
		||||
            expr,
 | 
			
		||||
            brace_token: _,
 | 
			
		||||
            arms,
 | 
			
		||||
        } = expr_match;
 | 
			
		||||
        self.require_normal_module(match_token);
 | 
			
		||||
        let mut state = HdlMatchParseState {
 | 
			
		||||
            errors: &mut self.errors,
 | 
			
		||||
            span,
 | 
			
		||||
        };
 | 
			
		||||
        let arms = Vec::from_iter(
 | 
			
		||||
            arms.into_iter()
 | 
			
		||||
                .filter_map(|arm| MatchArm::parse(&mut state, arm).ok()),
 | 
			
		||||
        );
 | 
			
		||||
        parse_quote_spanned! {span=>
 | 
			
		||||
            {
 | 
			
		||||
                type __MatchTy<V> = <<V as ::fayalite::expr::ToExpr>::Type as ::fayalite::ty::Type>::MatchVariant;
 | 
			
		||||
                let __match_expr = ::fayalite::expr::ToExpr::to_expr(&(#expr));
 | 
			
		||||
                ::fayalite::expr::check_match_expr(__match_expr, |__match_value, __infallible| {
 | 
			
		||||
                    #[allow(unused_variables)]
 | 
			
		||||
                    #check_match
 | 
			
		||||
                });
 | 
			
		||||
                for __match_variant in m.match_(__match_expr) {
 | 
			
		||||
                    let (__match_variant, __scope) = ::fayalite::ty::MatchVariantAndInactiveScope::match_activate_scope(__match_variant);
 | 
			
		||||
                    #match_token __match_variant {
 | 
			
		||||
                        #(#arms)*
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										746
									
								
								crates/fayalite-proc-macros-impl/src/value_derive_common.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										746
									
								
								crates/fayalite-proc-macros-impl/src/value_derive_common.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,746 @@
 | 
			
		|||
// SPDX-License-Identifier: LGPL-3.0-or-later
 | 
			
		||||
// See Notices.txt for copyright information
 | 
			
		||||
use crate::{fold::impl_fold, kw, Errors, HdlAttr};
 | 
			
		||||
use proc_macro2::{Span, TokenStream};
 | 
			
		||||
use quote::{format_ident, quote, quote_spanned, ToTokens};
 | 
			
		||||
use std::collections::{BTreeMap, HashMap, HashSet};
 | 
			
		||||
use syn::{
 | 
			
		||||
    fold::{fold_generics, Fold},
 | 
			
		||||
    parse::{Parse, ParseStream},
 | 
			
		||||
    parse_quote, parse_quote_spanned,
 | 
			
		||||
    punctuated::Punctuated,
 | 
			
		||||
    spanned::Spanned,
 | 
			
		||||
    token::{Brace, Paren, Where},
 | 
			
		||||
    Block, ConstParam, Expr, Field, Fields, FieldsNamed, FieldsUnnamed, GenericParam, Generics,
 | 
			
		||||
    Ident, Index, ItemImpl, Lifetime, LifetimeParam, Member, Path, Token, Type, TypeParam,
 | 
			
		||||
    TypePath, Visibility, WhereClause, WherePredicate,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug)]
 | 
			
		||||
pub(crate) struct Bounds(pub(crate) Punctuated<WherePredicate, Token![,]>);
 | 
			
		||||
 | 
			
		||||
impl_fold! {
 | 
			
		||||
    struct Bounds<>(Punctuated<WherePredicate, Token![,]>);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Parse for Bounds {
 | 
			
		||||
    fn parse(input: ParseStream) -> syn::Result<Self> {
 | 
			
		||||
        Ok(Bounds(Punctuated::parse_terminated(input)?))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<Option<WhereClause>> for Bounds {
 | 
			
		||||
    fn from(value: Option<WhereClause>) -> Self {
 | 
			
		||||
        Self(value.map_or_else(Punctuated::new, |v| v.predicates))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ToTokens for Bounds {
 | 
			
		||||
    fn to_tokens(&self, tokens: &mut TokenStream) {
 | 
			
		||||
        self.0.to_tokens(tokens)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
pub(crate) struct ParsedField<O> {
 | 
			
		||||
    pub(crate) options: HdlAttr<O>,
 | 
			
		||||
    pub(crate) vis: Visibility,
 | 
			
		||||
    pub(crate) name: Member,
 | 
			
		||||
    pub(crate) ty: Type,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<O> ParsedField<O> {
 | 
			
		||||
    pub(crate) fn var_name(&self) -> Ident {
 | 
			
		||||
        format_ident!("__v_{}", self.name)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) fn get_field_name(
 | 
			
		||||
    index: usize,
 | 
			
		||||
    name: Option<Ident>,
 | 
			
		||||
    ty_span: impl FnOnce() -> Span,
 | 
			
		||||
) -> Member {
 | 
			
		||||
    match name {
 | 
			
		||||
        Some(name) => Member::Named(name),
 | 
			
		||||
        None => Member::Unnamed(Index {
 | 
			
		||||
            index: index as _,
 | 
			
		||||
            span: ty_span(),
 | 
			
		||||
        }),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) fn get_field_names(fields: &Fields) -> impl Iterator<Item = Member> + '_ {
 | 
			
		||||
    fields
 | 
			
		||||
        .iter()
 | 
			
		||||
        .enumerate()
 | 
			
		||||
        .map(|(index, field)| get_field_name(index, field.ident.clone(), || field.ty.span()))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<O: Parse + Default> ParsedField<O> {
 | 
			
		||||
    pub(crate) fn parse_fields(
 | 
			
		||||
        errors: &mut Errors,
 | 
			
		||||
        fields: &mut Fields,
 | 
			
		||||
        in_enum: bool,
 | 
			
		||||
    ) -> (FieldsKind, Vec<ParsedField<O>>) {
 | 
			
		||||
        let mut unit_fields = Punctuated::new();
 | 
			
		||||
        let (fields_kind, fields) = match fields {
 | 
			
		||||
            Fields::Named(fields) => (FieldsKind::Named(fields.brace_token), &mut fields.named),
 | 
			
		||||
            Fields::Unnamed(fields) => {
 | 
			
		||||
                (FieldsKind::Unnamed(fields.paren_token), &mut fields.unnamed)
 | 
			
		||||
            }
 | 
			
		||||
            Fields::Unit => (FieldsKind::Unit, &mut unit_fields),
 | 
			
		||||
        };
 | 
			
		||||
        let fields = fields
 | 
			
		||||
            .iter_mut()
 | 
			
		||||
            .enumerate()
 | 
			
		||||
            .map(|(index, field)| {
 | 
			
		||||
                let options = errors
 | 
			
		||||
                    .unwrap_or_default(HdlAttr::parse_and_take_attr(&mut field.attrs))
 | 
			
		||||
                    .unwrap_or_default();
 | 
			
		||||
                let name = get_field_name(index, field.ident.clone(), || field.ty.span());
 | 
			
		||||
                if in_enum && !matches!(field.vis, Visibility::Inherited) {
 | 
			
		||||
                    errors.error(&field.vis, "field visibility not allowed in enums");
 | 
			
		||||
                }
 | 
			
		||||
                ParsedField {
 | 
			
		||||
                    options,
 | 
			
		||||
                    vis: field.vis.clone(),
 | 
			
		||||
                    name,
 | 
			
		||||
                    ty: field.ty.clone(),
 | 
			
		||||
                }
 | 
			
		||||
            })
 | 
			
		||||
            .collect();
 | 
			
		||||
        (fields_kind, fields)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Copy, Clone, Debug)]
 | 
			
		||||
pub(crate) enum FieldsKind {
 | 
			
		||||
    Unit,
 | 
			
		||||
    Named(Brace),
 | 
			
		||||
    Unnamed(Paren),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl FieldsKind {
 | 
			
		||||
    pub(crate) fn into_fields_named(
 | 
			
		||||
        brace_token: Brace,
 | 
			
		||||
        fields: impl IntoIterator<Item = syn::Field>,
 | 
			
		||||
    ) -> Fields {
 | 
			
		||||
        Fields::Named(FieldsNamed {
 | 
			
		||||
            brace_token,
 | 
			
		||||
            named: Punctuated::from_iter(fields),
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
    pub(crate) fn into_fields_unnamed(
 | 
			
		||||
        paren_token: Paren,
 | 
			
		||||
        fields: impl IntoIterator<Item = syn::Field>,
 | 
			
		||||
    ) -> Fields {
 | 
			
		||||
        Fields::Unnamed(FieldsUnnamed {
 | 
			
		||||
            paren_token,
 | 
			
		||||
            unnamed: Punctuated::from_iter(fields),
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
    pub(crate) fn into_fields(self, fields: impl IntoIterator<Item = syn::Field>) -> Fields {
 | 
			
		||||
        match self {
 | 
			
		||||
            FieldsKind::Unit => {
 | 
			
		||||
                let mut fields = fields.into_iter().peekable();
 | 
			
		||||
                let Some(first_field) = fields.peek() else {
 | 
			
		||||
                    return Fields::Unit;
 | 
			
		||||
                };
 | 
			
		||||
                if first_field.ident.is_some() {
 | 
			
		||||
                    Self::into_fields_named(Default::default(), fields)
 | 
			
		||||
                } else {
 | 
			
		||||
                    Self::into_fields_unnamed(Default::default(), fields)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            FieldsKind::Named(brace_token) => Self::into_fields_named(brace_token, fields),
 | 
			
		||||
            FieldsKind::Unnamed(paren_token) => Self::into_fields_unnamed(paren_token, fields),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) fn get_target(target: &Option<(kw::target, Paren, Path)>, item_ident: &Ident) -> Path {
 | 
			
		||||
    match target {
 | 
			
		||||
        Some((_, _, target)) => target.clone(),
 | 
			
		||||
        None => item_ident.clone().into(),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) struct ValueDeriveGenerics {
 | 
			
		||||
    pub(crate) generics: Generics,
 | 
			
		||||
    pub(crate) fixed_type_generics: Generics,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ValueDeriveGenerics {
 | 
			
		||||
    pub(crate) fn get(mut generics: Generics, where_: &Option<(Where, Paren, Bounds)>) -> Self {
 | 
			
		||||
        let mut fixed_type_generics = generics.clone();
 | 
			
		||||
        if let Some((_, _, bounds)) = where_ {
 | 
			
		||||
            generics
 | 
			
		||||
                .make_where_clause()
 | 
			
		||||
                .predicates
 | 
			
		||||
                .extend(bounds.0.iter().cloned());
 | 
			
		||||
            fixed_type_generics
 | 
			
		||||
                .where_clause
 | 
			
		||||
                .clone_from(&generics.where_clause);
 | 
			
		||||
        } else {
 | 
			
		||||
            let type_params = Vec::from_iter(generics.type_params().map(|v| v.ident.clone()));
 | 
			
		||||
            let predicates = &mut generics.make_where_clause().predicates;
 | 
			
		||||
            let fixed_type_predicates = &mut fixed_type_generics.make_where_clause().predicates;
 | 
			
		||||
            for type_param in type_params {
 | 
			
		||||
                predicates.push(parse_quote! {#type_param: ::fayalite::ty::Value});
 | 
			
		||||
                predicates.push(parse_quote! {<#type_param as ::fayalite::expr::ToExpr>::Type: ::fayalite::ty::Type<Value = #type_param>});
 | 
			
		||||
                fixed_type_predicates.push(parse_quote! {#type_param: ::fayalite::ty::Value});
 | 
			
		||||
                fixed_type_predicates.push(parse_quote! {<#type_param as ::fayalite::expr::ToExpr>::Type: ::fayalite::ty::FixedType<Value = #type_param>});
 | 
			
		||||
                fixed_type_predicates.push(parse_quote! {<<#type_param as ::fayalite::expr::ToExpr>::Type as ::fayalite::ty::Type>::MaskType: ::fayalite::ty::FixedType});
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        Self {
 | 
			
		||||
            generics,
 | 
			
		||||
            fixed_type_generics,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) fn derive_clone_hash_eq_partialeq_for_struct<Name: ToTokens>(
 | 
			
		||||
    the_struct_ident: &Ident,
 | 
			
		||||
    generics: &Generics,
 | 
			
		||||
    field_names: &[Name],
 | 
			
		||||
) -> TokenStream {
 | 
			
		||||
    let (impl_generics, type_generics, where_clause) = generics.split_for_impl();
 | 
			
		||||
    quote! {
 | 
			
		||||
        #[automatically_derived]
 | 
			
		||||
        impl #impl_generics ::fayalite::__std::clone::Clone for #the_struct_ident #type_generics
 | 
			
		||||
        #where_clause
 | 
			
		||||
        {
 | 
			
		||||
            fn clone(&self) -> Self {
 | 
			
		||||
                Self {
 | 
			
		||||
                    #(#field_names: ::fayalite::__std::clone::Clone::clone(&self.#field_names),)*
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[automatically_derived]
 | 
			
		||||
        impl #impl_generics ::fayalite::__std::hash::Hash for #the_struct_ident #type_generics
 | 
			
		||||
        #where_clause
 | 
			
		||||
        {
 | 
			
		||||
            #[allow(unused_variables)]
 | 
			
		||||
            fn hash<__H: ::fayalite::__std::hash::Hasher>(&self, hasher: &mut __H) {
 | 
			
		||||
                #(::fayalite::__std::hash::Hash::hash(&self.#field_names, hasher);)*
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[automatically_derived]
 | 
			
		||||
        impl #impl_generics ::fayalite::__std::cmp::Eq for #the_struct_ident #type_generics
 | 
			
		||||
        #where_clause
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[automatically_derived]
 | 
			
		||||
        impl #impl_generics ::fayalite::__std::cmp::PartialEq for #the_struct_ident #type_generics
 | 
			
		||||
        #where_clause
 | 
			
		||||
        {
 | 
			
		||||
            #[allow(unused_variables)]
 | 
			
		||||
            #[allow(clippy::nonminimal_bool)]
 | 
			
		||||
            fn eq(&self, other: &Self) -> ::fayalite::__std::primitive::bool {
 | 
			
		||||
                true
 | 
			
		||||
                #(&& ::fayalite::__std::cmp::PartialEq::eq(&self.#field_names, &other.#field_names))*
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) fn append_field(fields: &mut Fields, mut field: Field) -> Member {
 | 
			
		||||
    let ident = field.ident.clone().expect("ident is supplied");
 | 
			
		||||
    match fields {
 | 
			
		||||
        Fields::Named(FieldsNamed { named, .. }) => {
 | 
			
		||||
            named.push(field);
 | 
			
		||||
            Member::Named(ident)
 | 
			
		||||
        }
 | 
			
		||||
        Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => {
 | 
			
		||||
            field.ident = None;
 | 
			
		||||
            field.colon_token = None;
 | 
			
		||||
            let index = unnamed.len();
 | 
			
		||||
            unnamed.push(field);
 | 
			
		||||
            Member::Unnamed(index.into())
 | 
			
		||||
        }
 | 
			
		||||
        Fields::Unit => {
 | 
			
		||||
            *fields = Fields::Named(FieldsNamed {
 | 
			
		||||
                brace_token: Default::default(),
 | 
			
		||||
                named: Punctuated::from_iter([field]),
 | 
			
		||||
            });
 | 
			
		||||
            Member::Named(ident)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug)]
 | 
			
		||||
pub(crate) struct BuilderField {
 | 
			
		||||
    pub(crate) names: HashSet<Member>,
 | 
			
		||||
    pub(crate) mapped_value: Expr,
 | 
			
		||||
    pub(crate) mapped_type: Type,
 | 
			
		||||
    pub(crate) where_clause: Option<WhereClause>,
 | 
			
		||||
    pub(crate) builder_field_name: Ident,
 | 
			
		||||
    pub(crate) type_param: Ident,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub(crate) struct Builder {
 | 
			
		||||
    struct_name: Ident,
 | 
			
		||||
    vis: Visibility,
 | 
			
		||||
    fields: BTreeMap<String, BuilderField>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub(crate) struct BuilderWithFields {
 | 
			
		||||
    struct_name: Ident,
 | 
			
		||||
    vis: Visibility,
 | 
			
		||||
    phantom_type_param: Ident,
 | 
			
		||||
    phantom_type_field: Ident,
 | 
			
		||||
    fields: Vec<(String, BuilderField)>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Builder {
 | 
			
		||||
    pub(crate) fn new(struct_name: Ident, vis: Visibility) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            struct_name,
 | 
			
		||||
            vis,
 | 
			
		||||
            fields: BTreeMap::new(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub(crate) fn insert_field(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        name: Member,
 | 
			
		||||
        map_value: impl FnOnce(&Ident) -> Expr,
 | 
			
		||||
        map_type: impl FnOnce(&Ident) -> Type,
 | 
			
		||||
        where_clause: impl FnOnce(&Ident) -> Option<WhereClause>,
 | 
			
		||||
    ) {
 | 
			
		||||
        self.fields
 | 
			
		||||
            .entry(name.to_token_stream().to_string())
 | 
			
		||||
            .or_insert_with_key(|name| {
 | 
			
		||||
                let builder_field_name =
 | 
			
		||||
                    format_ident!("field_{}", name, span = self.struct_name.span());
 | 
			
		||||
                let type_param = format_ident!("__T_{}", name, span = self.struct_name.span());
 | 
			
		||||
                BuilderField {
 | 
			
		||||
                    names: HashSet::new(),
 | 
			
		||||
                    mapped_value: map_value(&builder_field_name),
 | 
			
		||||
                    mapped_type: map_type(&type_param),
 | 
			
		||||
                    where_clause: where_clause(&type_param),
 | 
			
		||||
                    builder_field_name,
 | 
			
		||||
                    type_param,
 | 
			
		||||
                }
 | 
			
		||||
            })
 | 
			
		||||
            .names
 | 
			
		||||
            .insert(name);
 | 
			
		||||
    }
 | 
			
		||||
    pub(crate) fn finish_filling_in_fields(self) -> BuilderWithFields {
 | 
			
		||||
        let Self {
 | 
			
		||||
            struct_name,
 | 
			
		||||
            vis,
 | 
			
		||||
            fields,
 | 
			
		||||
        } = self;
 | 
			
		||||
        let fields = Vec::from_iter(fields);
 | 
			
		||||
        BuilderWithFields {
 | 
			
		||||
            phantom_type_param: Ident::new("__Phantom", struct_name.span()),
 | 
			
		||||
            phantom_type_field: Ident::new("__phantom", struct_name.span()),
 | 
			
		||||
            struct_name,
 | 
			
		||||
            vis,
 | 
			
		||||
            fields,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl BuilderWithFields {
 | 
			
		||||
    pub(crate) fn get_field(&self, name: &Member) -> Option<(usize, &BuilderField)> {
 | 
			
		||||
        let index = self
 | 
			
		||||
            .fields
 | 
			
		||||
            .binary_search_by_key(&&*name.to_token_stream().to_string(), |v| &*v.0)
 | 
			
		||||
            .ok()?;
 | 
			
		||||
        Some((index, &self.fields[index].1))
 | 
			
		||||
    }
 | 
			
		||||
    pub(crate) fn ty(
 | 
			
		||||
        &self,
 | 
			
		||||
        specified_fields: impl IntoIterator<Item = (Member, Type)>,
 | 
			
		||||
        phantom_type: Option<&Type>,
 | 
			
		||||
        other_fields_are_any_type: bool,
 | 
			
		||||
    ) -> TypePath {
 | 
			
		||||
        let Self {
 | 
			
		||||
            struct_name,
 | 
			
		||||
            vis: _,
 | 
			
		||||
            phantom_type_param,
 | 
			
		||||
            phantom_type_field: _,
 | 
			
		||||
            fields,
 | 
			
		||||
        } = self;
 | 
			
		||||
        let span = struct_name.span();
 | 
			
		||||
        let mut arguments =
 | 
			
		||||
            Vec::from_iter(fields.iter().map(|(_, BuilderField { type_param, .. })| {
 | 
			
		||||
                if other_fields_are_any_type {
 | 
			
		||||
                    parse_quote_spanned! {span=>
 | 
			
		||||
                        #type_param
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    parse_quote_spanned! {span=>
 | 
			
		||||
                        ()
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }));
 | 
			
		||||
        for (name, ty) in specified_fields {
 | 
			
		||||
            let Some((index, _)) = self.get_field(&name) else {
 | 
			
		||||
                panic!("field not found: {}", name.to_token_stream());
 | 
			
		||||
            };
 | 
			
		||||
            arguments[index] = ty;
 | 
			
		||||
        }
 | 
			
		||||
        let phantom_type_param = phantom_type.is_none().then_some(phantom_type_param);
 | 
			
		||||
        parse_quote_spanned! {span=>
 | 
			
		||||
            #struct_name::<#phantom_type_param #phantom_type #(, #arguments)*>
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub(crate) fn append_generics(
 | 
			
		||||
        &self,
 | 
			
		||||
        specified_fields: impl IntoIterator<Item = Member>,
 | 
			
		||||
        has_phantom_type_param: bool,
 | 
			
		||||
        other_fields_are_any_type: bool,
 | 
			
		||||
        generics: &mut Generics,
 | 
			
		||||
    ) {
 | 
			
		||||
        let Self {
 | 
			
		||||
            struct_name: _,
 | 
			
		||||
            vis: _,
 | 
			
		||||
            phantom_type_param,
 | 
			
		||||
            phantom_type_field: _,
 | 
			
		||||
            fields,
 | 
			
		||||
        } = self;
 | 
			
		||||
        if has_phantom_type_param {
 | 
			
		||||
            generics.params.push(GenericParam::from(TypeParam::from(
 | 
			
		||||
                phantom_type_param.clone(),
 | 
			
		||||
            )));
 | 
			
		||||
        }
 | 
			
		||||
        if !other_fields_are_any_type {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        let mut type_params = Vec::from_iter(
 | 
			
		||||
            fields
 | 
			
		||||
                .iter()
 | 
			
		||||
                .map(|(_, BuilderField { type_param, .. })| Some(type_param)),
 | 
			
		||||
        );
 | 
			
		||||
        for name in specified_fields {
 | 
			
		||||
            let Some((index, _)) = self.get_field(&name) else {
 | 
			
		||||
                panic!("field not found: {}", name.to_token_stream());
 | 
			
		||||
            };
 | 
			
		||||
            type_params[index] = None;
 | 
			
		||||
        }
 | 
			
		||||
        generics.params.extend(
 | 
			
		||||
            type_params
 | 
			
		||||
                .into_iter()
 | 
			
		||||
                .filter_map(|v| Some(GenericParam::from(TypeParam::from(v?.clone())))),
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
    pub(crate) fn make_build_method(
 | 
			
		||||
        &self,
 | 
			
		||||
        build_fn_name: &Ident,
 | 
			
		||||
        specified_fields: impl IntoIterator<Item = (Member, Type)>,
 | 
			
		||||
        generics: &Generics,
 | 
			
		||||
        phantom_type: &Type,
 | 
			
		||||
        return_ty: &Type,
 | 
			
		||||
        mut body: Block,
 | 
			
		||||
    ) -> ItemImpl {
 | 
			
		||||
        let Self {
 | 
			
		||||
            struct_name,
 | 
			
		||||
            vis,
 | 
			
		||||
            phantom_type_param: _,
 | 
			
		||||
            phantom_type_field,
 | 
			
		||||
            fields,
 | 
			
		||||
        } = self;
 | 
			
		||||
        let span = struct_name.span();
 | 
			
		||||
        let field_names = Vec::from_iter(fields.iter().map(|v| &v.1.builder_field_name));
 | 
			
		||||
        let (impl_generics, _type_generics, where_clause) = generics.split_for_impl();
 | 
			
		||||
        let empty_arg = parse_quote_spanned! {span=>
 | 
			
		||||
            ()
 | 
			
		||||
        };
 | 
			
		||||
        let mut ty_arguments = vec![empty_arg; fields.len()];
 | 
			
		||||
        let empty_field_pat = quote_spanned! {span=>
 | 
			
		||||
            : _
 | 
			
		||||
        };
 | 
			
		||||
        let mut field_pats = vec![Some(empty_field_pat); fields.len()];
 | 
			
		||||
        for (name, ty) in specified_fields {
 | 
			
		||||
            let Some((index, _)) = self.get_field(&name) else {
 | 
			
		||||
                panic!("field not found: {}", name.to_token_stream());
 | 
			
		||||
            };
 | 
			
		||||
            ty_arguments[index] = ty;
 | 
			
		||||
            field_pats[index] = None;
 | 
			
		||||
        }
 | 
			
		||||
        body.stmts.insert(
 | 
			
		||||
            0,
 | 
			
		||||
            parse_quote_spanned! {span=>
 | 
			
		||||
                let Self {
 | 
			
		||||
                    #(#field_names #field_pats,)*
 | 
			
		||||
                    #phantom_type_field: _,
 | 
			
		||||
                } = self;
 | 
			
		||||
            },
 | 
			
		||||
        );
 | 
			
		||||
        parse_quote_spanned! {span=>
 | 
			
		||||
            #[automatically_derived]
 | 
			
		||||
            impl #impl_generics #struct_name<#phantom_type #(, #ty_arguments)*>
 | 
			
		||||
            #where_clause
 | 
			
		||||
            {
 | 
			
		||||
                #[allow(non_snake_case, dead_code)]
 | 
			
		||||
                #vis fn #build_fn_name(self) -> #return_ty
 | 
			
		||||
                #body
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ToTokens for BuilderWithFields {
 | 
			
		||||
    fn to_tokens(&self, tokens: &mut TokenStream) {
 | 
			
		||||
        let Self {
 | 
			
		||||
            struct_name,
 | 
			
		||||
            vis,
 | 
			
		||||
            phantom_type_param,
 | 
			
		||||
            phantom_type_field,
 | 
			
		||||
            fields,
 | 
			
		||||
        } = self;
 | 
			
		||||
        let span = struct_name.span();
 | 
			
		||||
        let mut any_generics = Generics::default();
 | 
			
		||||
        self.append_generics([], true, true, &mut any_generics);
 | 
			
		||||
        let empty_ty = self.ty([], None, false);
 | 
			
		||||
        let field_names = Vec::from_iter(fields.iter().map(|v| &v.1.builder_field_name));
 | 
			
		||||
        let field_type_params = Vec::from_iter(fields.iter().map(|v| &v.1.type_param));
 | 
			
		||||
        quote_spanned! {span=>
 | 
			
		||||
            #[allow(non_camel_case_types)]
 | 
			
		||||
            #[non_exhaustive]
 | 
			
		||||
            #vis struct #struct_name #any_generics {
 | 
			
		||||
                #(#field_names: #field_type_params,)*
 | 
			
		||||
                #phantom_type_field: ::fayalite::__std::marker::PhantomData<#phantom_type_param>,
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            #[automatically_derived]
 | 
			
		||||
            impl<#phantom_type_param> #empty_ty {
 | 
			
		||||
                fn new() -> Self {
 | 
			
		||||
                    Self {
 | 
			
		||||
                        #(#field_names: (),)*
 | 
			
		||||
                        #phantom_type_field: ::fayalite::__std::marker::PhantomData,
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        .to_tokens(tokens);
 | 
			
		||||
        for (field_index, (_, field)) in self.fields.iter().enumerate() {
 | 
			
		||||
            let initial_fields = &fields[..field_index];
 | 
			
		||||
            let final_fields = &fields[field_index..][1..];
 | 
			
		||||
            let initial_type_params =
 | 
			
		||||
                Vec::from_iter(initial_fields.iter().map(|v| &v.1.type_param));
 | 
			
		||||
            let final_type_params = Vec::from_iter(final_fields.iter().map(|v| &v.1.type_param));
 | 
			
		||||
            let initial_field_names =
 | 
			
		||||
                Vec::from_iter(initial_fields.iter().map(|v| &v.1.builder_field_name));
 | 
			
		||||
            let final_field_names =
 | 
			
		||||
                Vec::from_iter(final_fields.iter().map(|v| &v.1.builder_field_name));
 | 
			
		||||
            let BuilderField {
 | 
			
		||||
                names: _,
 | 
			
		||||
                mapped_value,
 | 
			
		||||
                mapped_type,
 | 
			
		||||
                where_clause,
 | 
			
		||||
                builder_field_name,
 | 
			
		||||
                type_param,
 | 
			
		||||
            } = field;
 | 
			
		||||
            quote_spanned! {span=>
 | 
			
		||||
                #[automatically_derived]
 | 
			
		||||
                #[allow(non_camel_case_types, dead_code)]
 | 
			
		||||
                impl<#phantom_type_param #(, #initial_type_params)* #(, #final_type_params)*> #struct_name<#phantom_type_param #(, #initial_type_params)*, () #(, #final_type_params)*> {
 | 
			
		||||
                    #vis fn #builder_field_name<#type_param>(self, #builder_field_name: #type_param) -> #struct_name<#phantom_type_param #(, #initial_type_params)*, #mapped_type #(, #final_type_params)*>
 | 
			
		||||
                    #where_clause
 | 
			
		||||
                    {
 | 
			
		||||
                        let Self {
 | 
			
		||||
                            #(#initial_field_names,)*
 | 
			
		||||
                            #builder_field_name: (),
 | 
			
		||||
                            #(#final_field_names,)*
 | 
			
		||||
                            #phantom_type_field: _,
 | 
			
		||||
                        } = self;
 | 
			
		||||
                        let #builder_field_name = #mapped_value;
 | 
			
		||||
                        #struct_name {
 | 
			
		||||
                            #(#field_names,)*
 | 
			
		||||
                            #phantom_type_field: ::fayalite::__std::marker::PhantomData,
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            .to_tokens(tokens);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) struct MapIdents {
 | 
			
		||||
    pub(crate) map: HashMap<Ident, Ident>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Fold for &MapIdents {
 | 
			
		||||
    fn fold_ident(&mut self, i: Ident) -> Ident {
 | 
			
		||||
        self.map.get(&i).cloned().unwrap_or(i)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) struct DupGenerics<M> {
 | 
			
		||||
    pub(crate) combined: Generics,
 | 
			
		||||
    pub(crate) maps: M,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) fn merge_punctuated<T, P: Default>(
 | 
			
		||||
    target: &mut Punctuated<T, P>,
 | 
			
		||||
    source: Punctuated<T, P>,
 | 
			
		||||
    make_punct: impl FnOnce() -> P,
 | 
			
		||||
) {
 | 
			
		||||
    if source.is_empty() {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    if target.is_empty() {
 | 
			
		||||
        *target = source;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    if !target.trailing_punct() {
 | 
			
		||||
        target.push_punct(make_punct());
 | 
			
		||||
    }
 | 
			
		||||
    target.extend(source.into_pairs());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) fn merge_generics(target: &mut Generics, source: Generics) {
 | 
			
		||||
    let Generics {
 | 
			
		||||
        lt_token,
 | 
			
		||||
        params,
 | 
			
		||||
        gt_token,
 | 
			
		||||
        where_clause,
 | 
			
		||||
    } = source;
 | 
			
		||||
    let span = lt_token.map(|v| v.span).unwrap_or_else(|| params.span());
 | 
			
		||||
    target.lt_token = target.lt_token.or(lt_token);
 | 
			
		||||
    merge_punctuated(&mut target.params, params, || Token);
 | 
			
		||||
    target.gt_token = target.gt_token.or(gt_token);
 | 
			
		||||
    if let Some(where_clause) = where_clause {
 | 
			
		||||
        if let Some(target_where_clause) = &mut target.where_clause {
 | 
			
		||||
            let WhereClause {
 | 
			
		||||
                where_token,
 | 
			
		||||
                predicates,
 | 
			
		||||
            } = where_clause;
 | 
			
		||||
            let span = where_token.span;
 | 
			
		||||
            target_where_clause.where_token = where_token;
 | 
			
		||||
            merge_punctuated(&mut target_where_clause.predicates, predicates, || {
 | 
			
		||||
                Token
 | 
			
		||||
            });
 | 
			
		||||
        } else {
 | 
			
		||||
            target.where_clause = Some(where_clause);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl DupGenerics<Vec<MapIdents>> {
 | 
			
		||||
    pub(crate) fn new_dyn(generics: &Generics, count: usize) -> Self {
 | 
			
		||||
        let mut maps = Vec::from_iter((0..count).map(|_| MapIdents {
 | 
			
		||||
            map: HashMap::new(),
 | 
			
		||||
        }));
 | 
			
		||||
        for param in &generics.params {
 | 
			
		||||
            let (GenericParam::Lifetime(LifetimeParam {
 | 
			
		||||
                lifetime: Lifetime { ident, .. },
 | 
			
		||||
                ..
 | 
			
		||||
            })
 | 
			
		||||
            | GenericParam::Type(TypeParam { ident, .. })
 | 
			
		||||
            | GenericParam::Const(ConstParam { ident, .. })) = param;
 | 
			
		||||
            for (i, map_idents) in maps.iter_mut().enumerate() {
 | 
			
		||||
                map_idents
 | 
			
		||||
                    .map
 | 
			
		||||
                    .insert(ident.clone(), format_ident!("__{}_{}", ident, i));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        let mut combined = Generics::default();
 | 
			
		||||
        for map_idents in maps.iter() {
 | 
			
		||||
            merge_generics(
 | 
			
		||||
                &mut combined,
 | 
			
		||||
                fold_generics(&mut { map_idents }, generics.clone()),
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
        Self { combined, maps }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<const COUNT: usize> DupGenerics<[MapIdents; COUNT]> {
 | 
			
		||||
    pub(crate) fn new(generics: &Generics) -> Self {
 | 
			
		||||
        let DupGenerics { combined, maps } = DupGenerics::new_dyn(generics, COUNT);
 | 
			
		||||
        Self {
 | 
			
		||||
            combined,
 | 
			
		||||
            maps: maps.try_into().ok().unwrap(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) fn add_where_predicate(
 | 
			
		||||
    target: &mut Generics,
 | 
			
		||||
    span: Span,
 | 
			
		||||
    where_predicate: WherePredicate,
 | 
			
		||||
) {
 | 
			
		||||
    let WhereClause {
 | 
			
		||||
        where_token: _,
 | 
			
		||||
        predicates,
 | 
			
		||||
    } = target.where_clause.get_or_insert_with(|| WhereClause {
 | 
			
		||||
        where_token: Token,
 | 
			
		||||
        predicates: Punctuated::new(),
 | 
			
		||||
    });
 | 
			
		||||
    if !predicates.empty_or_trailing() {
 | 
			
		||||
        predicates.push_punct(Token);
 | 
			
		||||
    }
 | 
			
		||||
    predicates.push_value(where_predicate);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) fn make_connect_impl(
 | 
			
		||||
    connect_inexact: Option<(crate::kw::connect_inexact,)>,
 | 
			
		||||
    generics: &Generics,
 | 
			
		||||
    ty_ident: &Ident,
 | 
			
		||||
    field_types: impl IntoIterator<Item = Type>,
 | 
			
		||||
) -> TokenStream {
 | 
			
		||||
    let span = ty_ident.span();
 | 
			
		||||
    let impl_generics;
 | 
			
		||||
    let combined_generics;
 | 
			
		||||
    let where_clause;
 | 
			
		||||
    let lhs_generics;
 | 
			
		||||
    let lhs_type_generics;
 | 
			
		||||
    let rhs_generics;
 | 
			
		||||
    let rhs_type_generics;
 | 
			
		||||
    if connect_inexact.is_some() {
 | 
			
		||||
        let DupGenerics {
 | 
			
		||||
            mut combined,
 | 
			
		||||
            maps: [lhs_map, rhs_map],
 | 
			
		||||
        } = DupGenerics::new(generics);
 | 
			
		||||
        for field_type in field_types {
 | 
			
		||||
            let lhs_type = (&lhs_map).fold_type(field_type.clone());
 | 
			
		||||
            let rhs_type = (&rhs_map).fold_type(field_type);
 | 
			
		||||
            add_where_predicate(
 | 
			
		||||
                &mut combined,
 | 
			
		||||
                span,
 | 
			
		||||
                parse_quote_spanned! {span=>
 | 
			
		||||
                    #lhs_type: ::fayalite::ty::Connect<#rhs_type>
 | 
			
		||||
                },
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
        combined_generics = combined;
 | 
			
		||||
        (impl_generics, _, where_clause) = combined_generics.split_for_impl();
 | 
			
		||||
        lhs_generics = (&lhs_map).fold_generics(generics.clone());
 | 
			
		||||
        (_, lhs_type_generics, _) = lhs_generics.split_for_impl();
 | 
			
		||||
        rhs_generics = (&rhs_map).fold_generics(generics.clone());
 | 
			
		||||
        (_, rhs_type_generics, _) = rhs_generics.split_for_impl();
 | 
			
		||||
    } else {
 | 
			
		||||
        let mut generics = generics.clone();
 | 
			
		||||
        for field_type in field_types {
 | 
			
		||||
            add_where_predicate(
 | 
			
		||||
                &mut generics,
 | 
			
		||||
                span,
 | 
			
		||||
                parse_quote_spanned! {span=>
 | 
			
		||||
                    #field_type: ::fayalite::ty::Connect<#field_type>
 | 
			
		||||
                },
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
        combined_generics = generics;
 | 
			
		||||
        (impl_generics, lhs_type_generics, where_clause) = combined_generics.split_for_impl();
 | 
			
		||||
        rhs_type_generics = lhs_type_generics.clone();
 | 
			
		||||
    }
 | 
			
		||||
    quote_spanned! {span=>
 | 
			
		||||
        #[automatically_derived]
 | 
			
		||||
        #[allow(non_camel_case_types)]
 | 
			
		||||
        impl #impl_generics ::fayalite::ty::Connect<#ty_ident #rhs_type_generics> for #ty_ident #lhs_type_generics
 | 
			
		||||
        #where_clause
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										901
									
								
								crates/fayalite-proc-macros-impl/src/value_derive_enum.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										901
									
								
								crates/fayalite-proc-macros-impl/src/value_derive_enum.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,901 @@
 | 
			
		|||
// SPDX-License-Identifier: LGPL-3.0-or-later
 | 
			
		||||
// See Notices.txt for copyright information
 | 
			
		||||
use crate::{
 | 
			
		||||
    value_derive_common::{
 | 
			
		||||
        append_field, derive_clone_hash_eq_partialeq_for_struct, get_field_names, get_target,
 | 
			
		||||
        make_connect_impl, Bounds, Builder, FieldsKind, ParsedField, ValueDeriveGenerics,
 | 
			
		||||
    },
 | 
			
		||||
    value_derive_struct::{self, ParsedStruct, ParsedStructNames, StructOptions},
 | 
			
		||||
    Errors, HdlAttr,
 | 
			
		||||
};
 | 
			
		||||
use proc_macro2::TokenStream;
 | 
			
		||||
use quote::{format_ident, quote, quote_spanned, ToTokens};
 | 
			
		||||
use syn::{
 | 
			
		||||
    parse_quote, parse_quote_spanned, punctuated::Punctuated, spanned::Spanned, token::Brace,
 | 
			
		||||
    Field, FieldMutability, Fields, FieldsNamed, Generics, Ident, Index, ItemEnum, ItemStruct,
 | 
			
		||||
    Member, Path, Token, Type, Variant, Visibility,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
crate::options! {
 | 
			
		||||
    #[options = EnumOptions]
 | 
			
		||||
    enum EnumOption {
 | 
			
		||||
        OutlineGenerated(outline_generated),
 | 
			
		||||
        ConnectInexact(connect_inexact),
 | 
			
		||||
        Bounds(where_, Bounds),
 | 
			
		||||
        Target(target, Path),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
crate::options! {
 | 
			
		||||
    #[options = VariantOptions]
 | 
			
		||||
    enum VariantOption {}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
crate::options! {
 | 
			
		||||
    #[options = FieldOptions]
 | 
			
		||||
    enum FieldOption {}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
enum VariantValue {
 | 
			
		||||
    None,
 | 
			
		||||
    Direct {
 | 
			
		||||
        value_type: Type,
 | 
			
		||||
    },
 | 
			
		||||
    Struct {
 | 
			
		||||
        value_struct: ItemStruct,
 | 
			
		||||
        parsed_struct: ParsedStruct,
 | 
			
		||||
    },
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl VariantValue {
 | 
			
		||||
    fn is_none(&self) -> bool {
 | 
			
		||||
        matches!(self, Self::None)
 | 
			
		||||
    }
 | 
			
		||||
    fn value_ty(&self) -> Option<Type> {
 | 
			
		||||
        match self {
 | 
			
		||||
            VariantValue::None => None,
 | 
			
		||||
            VariantValue::Direct { value_type } => Some(value_type.clone()),
 | 
			
		||||
            VariantValue::Struct { value_struct, .. } => {
 | 
			
		||||
                let (_, type_generics, _) = value_struct.generics.split_for_impl();
 | 
			
		||||
                let ident = &value_struct.ident;
 | 
			
		||||
                Some(parse_quote! { #ident #type_generics })
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct ParsedVariant {
 | 
			
		||||
    options: HdlAttr<VariantOptions>,
 | 
			
		||||
    ident: Ident,
 | 
			
		||||
    fields_kind: FieldsKind,
 | 
			
		||||
    fields: Vec<ParsedField<FieldOptions>>,
 | 
			
		||||
    value: VariantValue,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ParsedVariant {
 | 
			
		||||
    fn parse(
 | 
			
		||||
        errors: &mut Errors,
 | 
			
		||||
        variant: Variant,
 | 
			
		||||
        enum_options: &EnumOptions,
 | 
			
		||||
        enum_vis: &Visibility,
 | 
			
		||||
        enum_ident: &Ident,
 | 
			
		||||
        enum_generics: &Generics,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        let target = get_target(&enum_options.target, enum_ident);
 | 
			
		||||
        let Variant {
 | 
			
		||||
            mut attrs,
 | 
			
		||||
            ident,
 | 
			
		||||
            fields,
 | 
			
		||||
            discriminant,
 | 
			
		||||
        } = variant;
 | 
			
		||||
        if let Some((eq, _)) = discriminant {
 | 
			
		||||
            errors.error(eq, "#[derive(Value)]: discriminants not allowed");
 | 
			
		||||
        }
 | 
			
		||||
        let variant_options = errors
 | 
			
		||||
            .unwrap_or_default(HdlAttr::parse_and_take_attr(&mut attrs))
 | 
			
		||||
            .unwrap_or_default();
 | 
			
		||||
        let (fields_kind, parsed_fields) =
 | 
			
		||||
            ParsedField::parse_fields(errors, &mut fields.clone(), true);
 | 
			
		||||
        let value = match (&fields_kind, &*parsed_fields) {
 | 
			
		||||
            (FieldsKind::Unit, _) => VariantValue::None,
 | 
			
		||||
            (
 | 
			
		||||
                FieldsKind::Unnamed(_),
 | 
			
		||||
                [ParsedField {
 | 
			
		||||
                    options,
 | 
			
		||||
                    vis: _,
 | 
			
		||||
                    name: Member::Unnamed(Index { index: 0, span: _ }),
 | 
			
		||||
                    ty,
 | 
			
		||||
                }],
 | 
			
		||||
            ) => {
 | 
			
		||||
                let FieldOptions {} = options.body;
 | 
			
		||||
                VariantValue::Direct {
 | 
			
		||||
                    value_type: ty.clone(),
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            _ => {
 | 
			
		||||
                let variant_value_struct_ident =
 | 
			
		||||
                    format_ident!("__{}__{}", enum_ident, ident, span = ident.span());
 | 
			
		||||
                let variant_type_struct_ident =
 | 
			
		||||
                    format_ident!("__{}__{}__Type", enum_ident, ident, span = ident.span());
 | 
			
		||||
                let mut value_struct_fields = fields.clone();
 | 
			
		||||
                let (_, type_generics, _) = enum_generics.split_for_impl();
 | 
			
		||||
                append_field(
 | 
			
		||||
                    &mut value_struct_fields,
 | 
			
		||||
                    Field {
 | 
			
		||||
                        attrs: vec![HdlAttr::from(value_derive_struct::FieldOptions {
 | 
			
		||||
                            flip: None,
 | 
			
		||||
                            skip: Some(Default::default()),
 | 
			
		||||
                        })
 | 
			
		||||
                        .to_attr()],
 | 
			
		||||
                        vis: enum_vis.clone(),
 | 
			
		||||
                        mutability: FieldMutability::None,
 | 
			
		||||
                        ident: Some(Ident::new("__phantom", ident.span())),
 | 
			
		||||
                        colon_token: None,
 | 
			
		||||
                        ty: parse_quote_spanned! {ident.span()=>
 | 
			
		||||
                            ::fayalite::__std::marker::PhantomData<#target #type_generics>
 | 
			
		||||
                        },
 | 
			
		||||
                    },
 | 
			
		||||
                );
 | 
			
		||||
                let (value_struct_fields_kind, value_struct_parsed_fields) =
 | 
			
		||||
                    ParsedField::parse_fields(errors, &mut value_struct_fields, false);
 | 
			
		||||
                let value_struct = ItemStruct {
 | 
			
		||||
                    attrs: vec![parse_quote! { #[allow(non_camel_case_types)] }],
 | 
			
		||||
                    vis: enum_vis.clone(),
 | 
			
		||||
                    struct_token: Token),
 | 
			
		||||
                    ident: variant_value_struct_ident.clone(),
 | 
			
		||||
                    generics: enum_generics.clone(),
 | 
			
		||||
                    fields: value_struct_fields,
 | 
			
		||||
                    semi_token: None,
 | 
			
		||||
                };
 | 
			
		||||
                VariantValue::Struct {
 | 
			
		||||
                    value_struct,
 | 
			
		||||
                    parsed_struct: ParsedStruct {
 | 
			
		||||
                        options: StructOptions {
 | 
			
		||||
                            outline_generated: None,
 | 
			
		||||
                            fixed_type: Some(Default::default()),
 | 
			
		||||
                            where_: Some((
 | 
			
		||||
                                Default::default(),
 | 
			
		||||
                                Default::default(),
 | 
			
		||||
                                ValueDeriveGenerics::get(
 | 
			
		||||
                                    enum_generics.clone(),
 | 
			
		||||
                                    &enum_options.where_,
 | 
			
		||||
                                )
 | 
			
		||||
                                .fixed_type_generics
 | 
			
		||||
                                .where_clause
 | 
			
		||||
                                .into(),
 | 
			
		||||
                            )),
 | 
			
		||||
                            target: None,
 | 
			
		||||
                            connect_inexact: enum_options.connect_inexact,
 | 
			
		||||
                        }
 | 
			
		||||
                        .into(),
 | 
			
		||||
                        vis: enum_vis.clone(),
 | 
			
		||||
                        struct_token: Default::default(),
 | 
			
		||||
                        generics: enum_generics.clone(),
 | 
			
		||||
                        fields_kind: value_struct_fields_kind,
 | 
			
		||||
                        fields: value_struct_parsed_fields,
 | 
			
		||||
                        semi_token: None, // it will fill in the semicolon if needed
 | 
			
		||||
                        skip_check_fields: true,
 | 
			
		||||
                        names: ParsedStructNames {
 | 
			
		||||
                            ident: variant_value_struct_ident.clone(),
 | 
			
		||||
                            type_struct_debug_ident: Some(format!("{enum_ident}::{ident}::Type")),
 | 
			
		||||
                            type_struct_ident: variant_type_struct_ident,
 | 
			
		||||
                            match_variant_ident: None,
 | 
			
		||||
                            builder_struct_ident: None,
 | 
			
		||||
                            mask_match_variant_ident: None,
 | 
			
		||||
                            mask_type_ident: None,
 | 
			
		||||
                            mask_type_debug_ident: Some(format!(
 | 
			
		||||
                                "AsMask<{enum_ident}::{ident}>::Type"
 | 
			
		||||
                            )),
 | 
			
		||||
                            mask_value_ident: None,
 | 
			
		||||
                            mask_value_debug_ident: Some(format!("AsMask<{enum_ident}::{ident}>")),
 | 
			
		||||
                            mask_builder_struct_ident: None,
 | 
			
		||||
                        },
 | 
			
		||||
                    },
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
        ParsedVariant {
 | 
			
		||||
            options: variant_options,
 | 
			
		||||
            ident,
 | 
			
		||||
            fields_kind,
 | 
			
		||||
            fields: parsed_fields,
 | 
			
		||||
            value,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct ParsedEnum {
 | 
			
		||||
    options: HdlAttr<EnumOptions>,
 | 
			
		||||
    vis: Visibility,
 | 
			
		||||
    enum_token: Token![enum],
 | 
			
		||||
    ident: Ident,
 | 
			
		||||
    generics: Generics,
 | 
			
		||||
    brace_token: Brace,
 | 
			
		||||
    variants: Vec<ParsedVariant>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ParsedEnum {
 | 
			
		||||
    fn parse(item: ItemEnum) -> syn::Result<Self> {
 | 
			
		||||
        let ItemEnum {
 | 
			
		||||
            mut attrs,
 | 
			
		||||
            vis,
 | 
			
		||||
            enum_token,
 | 
			
		||||
            ident,
 | 
			
		||||
            generics,
 | 
			
		||||
            brace_token,
 | 
			
		||||
            variants,
 | 
			
		||||
        } = item;
 | 
			
		||||
        let mut errors = Errors::new();
 | 
			
		||||
        let enum_options = errors
 | 
			
		||||
            .unwrap_or_default(HdlAttr::parse_and_take_attr(&mut attrs))
 | 
			
		||||
            .unwrap_or_default();
 | 
			
		||||
        let variants = variants
 | 
			
		||||
            .into_iter()
 | 
			
		||||
            .map(|variant| {
 | 
			
		||||
                ParsedVariant::parse(
 | 
			
		||||
                    &mut errors,
 | 
			
		||||
                    variant,
 | 
			
		||||
                    &enum_options.body,
 | 
			
		||||
                    &vis,
 | 
			
		||||
                    &ident,
 | 
			
		||||
                    &generics,
 | 
			
		||||
                )
 | 
			
		||||
            })
 | 
			
		||||
            .collect();
 | 
			
		||||
        errors.finish()?;
 | 
			
		||||
        Ok(ParsedEnum {
 | 
			
		||||
            options: enum_options,
 | 
			
		||||
            vis,
 | 
			
		||||
            enum_token,
 | 
			
		||||
            ident,
 | 
			
		||||
            generics,
 | 
			
		||||
            brace_token,
 | 
			
		||||
            variants,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ToTokens for ParsedEnum {
 | 
			
		||||
    fn to_tokens(&self, tokens: &mut TokenStream) {
 | 
			
		||||
        let Self {
 | 
			
		||||
            options,
 | 
			
		||||
            vis,
 | 
			
		||||
            enum_token,
 | 
			
		||||
            ident: enum_ident,
 | 
			
		||||
            generics: enum_generics,
 | 
			
		||||
            brace_token,
 | 
			
		||||
            variants,
 | 
			
		||||
        } = self;
 | 
			
		||||
        let EnumOptions {
 | 
			
		||||
            outline_generated: _,
 | 
			
		||||
            connect_inexact,
 | 
			
		||||
            where_,
 | 
			
		||||
            target,
 | 
			
		||||
        } = &options.body;
 | 
			
		||||
        let target = get_target(target, enum_ident);
 | 
			
		||||
        let ValueDeriveGenerics {
 | 
			
		||||
            generics: _,
 | 
			
		||||
            fixed_type_generics,
 | 
			
		||||
        } = ValueDeriveGenerics::get(enum_generics.clone(), where_);
 | 
			
		||||
        let (fixed_type_impl_generics, fixed_type_type_generics, fixed_type_where_clause) =
 | 
			
		||||
            fixed_type_generics.split_for_impl();
 | 
			
		||||
        let type_struct_ident = format_ident!("__{}__Type", enum_ident);
 | 
			
		||||
        let mut field_checks = vec![];
 | 
			
		||||
        let mut make_type_struct_variant_type = |variant: &ParsedVariant| {
 | 
			
		||||
            let VariantOptions {} = variant.options.body;
 | 
			
		||||
            let (value_struct, parsed_struct) = match &variant.value {
 | 
			
		||||
                VariantValue::None => {
 | 
			
		||||
                    return None;
 | 
			
		||||
                }
 | 
			
		||||
                VariantValue::Direct { value_type } => {
 | 
			
		||||
                    field_checks.push(quote_spanned! {value_type.span()=>
 | 
			
		||||
                        __check_field::<#value_type>();
 | 
			
		||||
                    });
 | 
			
		||||
                    return Some(parse_quote! { <#value_type as ::fayalite::expr::ToExpr>::Type });
 | 
			
		||||
                }
 | 
			
		||||
                VariantValue::Struct {
 | 
			
		||||
                    value_struct,
 | 
			
		||||
                    parsed_struct,
 | 
			
		||||
                } => (value_struct, parsed_struct),
 | 
			
		||||
            };
 | 
			
		||||
            value_struct.to_tokens(tokens);
 | 
			
		||||
            parsed_struct.to_tokens(tokens);
 | 
			
		||||
            let mut field_names = Vec::from_iter(get_field_names(&value_struct.fields));
 | 
			
		||||
            derive_clone_hash_eq_partialeq_for_struct(
 | 
			
		||||
                &value_struct.ident,
 | 
			
		||||
                &fixed_type_generics,
 | 
			
		||||
                &field_names,
 | 
			
		||||
            )
 | 
			
		||||
            .to_tokens(tokens);
 | 
			
		||||
            field_names = Vec::from_iter(
 | 
			
		||||
                field_names
 | 
			
		||||
                    .into_iter()
 | 
			
		||||
                    .zip(parsed_struct.fields.iter())
 | 
			
		||||
                    .filter_map(|(member, field)| {
 | 
			
		||||
                        field.options.body.skip.is_none().then_some(member)
 | 
			
		||||
                    }),
 | 
			
		||||
            );
 | 
			
		||||
            let field_name_strs =
 | 
			
		||||
                Vec::from_iter(field_names.iter().map(|v| v.to_token_stream().to_string()));
 | 
			
		||||
            let debug_ident = format!("{enum_ident}::{}", variant.ident);
 | 
			
		||||
            let debug_body = match variant.fields_kind {
 | 
			
		||||
                FieldsKind::Unit => quote! {
 | 
			
		||||
                    f.debug_struct(#debug_ident).finish()
 | 
			
		||||
                },
 | 
			
		||||
                FieldsKind::Named(_) => quote! {
 | 
			
		||||
                    f.debug_struct(#debug_ident)#(.field(#field_name_strs, &self.#field_names))*.finish()
 | 
			
		||||
                },
 | 
			
		||||
                FieldsKind::Unnamed(_) => quote! {
 | 
			
		||||
                    f.debug_tuple(#debug_ident)#(.field(&self.#field_names))*.finish()
 | 
			
		||||
                },
 | 
			
		||||
            };
 | 
			
		||||
            let value_struct_ident = &value_struct.ident;
 | 
			
		||||
            quote! {
 | 
			
		||||
                #[automatically_derived]
 | 
			
		||||
                impl #fixed_type_impl_generics ::fayalite::__std::fmt::Debug for #value_struct_ident #fixed_type_type_generics
 | 
			
		||||
                #fixed_type_where_clause
 | 
			
		||||
                {
 | 
			
		||||
                    fn fmt(&self, f: &mut ::fayalite::__std::fmt::Formatter<'_>) -> ::fayalite::__std::fmt::Result {
 | 
			
		||||
                        #debug_body
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }.to_tokens(tokens);
 | 
			
		||||
            Some(
 | 
			
		||||
                parse_quote! { <#value_struct_ident #fixed_type_type_generics as ::fayalite::expr::ToExpr>::Type },
 | 
			
		||||
            )
 | 
			
		||||
        };
 | 
			
		||||
        let type_struct_variants = Punctuated::from_iter(variants.iter().filter_map(|variant| {
 | 
			
		||||
            let VariantOptions {} = variant.options.body;
 | 
			
		||||
            Some(Field {
 | 
			
		||||
                attrs: vec![],
 | 
			
		||||
                vis: vis.clone(),
 | 
			
		||||
                mutability: FieldMutability::None,
 | 
			
		||||
                ident: Some(variant.ident.clone()),
 | 
			
		||||
                colon_token: None, // it will fill in the colon if needed
 | 
			
		||||
                ty: make_type_struct_variant_type(variant)?,
 | 
			
		||||
            })
 | 
			
		||||
        }));
 | 
			
		||||
        let type_struct = ItemStruct {
 | 
			
		||||
            attrs: vec![
 | 
			
		||||
                parse_quote! {#[allow(non_camel_case_types)]},
 | 
			
		||||
                parse_quote! {#[allow(non_snake_case)]},
 | 
			
		||||
            ],
 | 
			
		||||
            vis: vis.clone(),
 | 
			
		||||
            struct_token: Token,
 | 
			
		||||
            ident: type_struct_ident,
 | 
			
		||||
            generics: fixed_type_generics.clone(),
 | 
			
		||||
            fields: Fields::Named(FieldsNamed {
 | 
			
		||||
                brace_token: *brace_token,
 | 
			
		||||
                named: type_struct_variants,
 | 
			
		||||
            }),
 | 
			
		||||
            semi_token: None,
 | 
			
		||||
        };
 | 
			
		||||
        let type_struct_ident = &type_struct.ident;
 | 
			
		||||
        let type_struct_debug_ident = format!("{enum_ident}::Type");
 | 
			
		||||
        type_struct.to_tokens(tokens);
 | 
			
		||||
        let non_empty_variant_names = Vec::from_iter(
 | 
			
		||||
            variants
 | 
			
		||||
                .iter()
 | 
			
		||||
                .filter(|v| !v.value.is_none())
 | 
			
		||||
                .map(|v| v.ident.clone()),
 | 
			
		||||
        );
 | 
			
		||||
        let non_empty_variant_name_strs =
 | 
			
		||||
            Vec::from_iter(non_empty_variant_names.iter().map(|v| v.to_string()));
 | 
			
		||||
        let debug_type_body = quote! {
 | 
			
		||||
            f.debug_struct(#type_struct_debug_ident)#(.field(#non_empty_variant_name_strs, &self.#non_empty_variant_names))*.finish()
 | 
			
		||||
        };
 | 
			
		||||
        derive_clone_hash_eq_partialeq_for_struct(
 | 
			
		||||
            type_struct_ident,
 | 
			
		||||
            &fixed_type_generics,
 | 
			
		||||
            &non_empty_variant_names,
 | 
			
		||||
        )
 | 
			
		||||
        .to_tokens(tokens);
 | 
			
		||||
        let variant_names = Vec::from_iter(variants.iter().map(|v| &v.ident));
 | 
			
		||||
        let variant_name_strs = Vec::from_iter(variant_names.iter().map(|v| v.to_string()));
 | 
			
		||||
        let (variant_field_pats, variant_to_canonical_values): (Vec<_>, Vec<_>) = variants
 | 
			
		||||
            .iter()
 | 
			
		||||
            .map(|v| {
 | 
			
		||||
                let field_names: Vec<_> = v.fields.iter().map(|field| &field.name).collect();
 | 
			
		||||
                let var_names: Vec<_> = v.fields.iter().map(|field| field.var_name()).collect();
 | 
			
		||||
                let field_pats = quote! {
 | 
			
		||||
                    #(#field_names: #var_names,)*
 | 
			
		||||
                };
 | 
			
		||||
                let to_canonical_value = match &v.value {
 | 
			
		||||
                    VariantValue::None => quote! { ::fayalite::__std::option::Option::None },
 | 
			
		||||
                    VariantValue::Direct { .. } => {
 | 
			
		||||
                        debug_assert_eq!(var_names.len(), 1);
 | 
			
		||||
                        quote! {
 | 
			
		||||
                            ::fayalite::__std::option::Option::Some(
 | 
			
		||||
                                ::fayalite::ty::DynValueTrait::to_canonical_dyn(#(#var_names)*),
 | 
			
		||||
                            )
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    VariantValue::Struct {
 | 
			
		||||
                        value_struct,
 | 
			
		||||
                        parsed_struct,
 | 
			
		||||
                    } => {
 | 
			
		||||
                        let value_struct_ident = &value_struct.ident;
 | 
			
		||||
                        let phantom_field_name = &parsed_struct.fields.last().expect("missing phantom field").name;
 | 
			
		||||
                        let type_generics = fixed_type_type_generics.as_turbofish();
 | 
			
		||||
                        quote! {
 | 
			
		||||
                            ::fayalite::__std::option::Option::Some(
 | 
			
		||||
                                ::fayalite::ty::DynValueTrait::to_canonical_dyn(
 | 
			
		||||
                                    &#value_struct_ident #type_generics {
 | 
			
		||||
                                        #(#field_names: ::fayalite::__std::clone::Clone::clone(#var_names),)*
 | 
			
		||||
                                        #phantom_field_name: ::fayalite::__std::marker::PhantomData,
 | 
			
		||||
                                    },
 | 
			
		||||
                                ),
 | 
			
		||||
                            )
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
                (field_pats, to_canonical_value)
 | 
			
		||||
            })
 | 
			
		||||
            .unzip();
 | 
			
		||||
        let mut match_enum_variants = Punctuated::new();
 | 
			
		||||
        let mut match_enum_debug_arms = vec![];
 | 
			
		||||
        let mut match_enum_arms = vec![];
 | 
			
		||||
        let mut variant_vars = vec![];
 | 
			
		||||
        let mut from_canonical_type_variant_lets = vec![];
 | 
			
		||||
        let mut non_empty_variant_vars = vec![];
 | 
			
		||||
        let mut enum_type_variants = vec![];
 | 
			
		||||
        let mut enum_type_variants_hint = vec![];
 | 
			
		||||
        let match_enum_ident = format_ident!("__{}__MatchEnum", enum_ident);
 | 
			
		||||
        let mut builder = Builder::new(format_ident!("__{}__Builder", enum_ident), vis.clone());
 | 
			
		||||
        for variant in variants.iter() {
 | 
			
		||||
            for field in variant.fields.iter() {
 | 
			
		||||
                builder.insert_field(
 | 
			
		||||
                    field.name.clone(),
 | 
			
		||||
                    |v| {
 | 
			
		||||
                        parse_quote_spanned! {v.span()=>
 | 
			
		||||
                            ::fayalite::expr::ToExpr::to_expr(&#v)
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                    |t| {
 | 
			
		||||
                        parse_quote_spanned! {t.span()=>
 | 
			
		||||
                            ::fayalite::expr::Expr<<<#t as ::fayalite::expr::ToExpr>::Type as ::fayalite::ty::Type>::Value>
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                    |t| {
 | 
			
		||||
                        parse_quote_spanned! {t.span()=>
 | 
			
		||||
                            where
 | 
			
		||||
                                #t: ::fayalite::expr::ToExpr,
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        let builder = builder.finish_filling_in_fields();
 | 
			
		||||
        builder.to_tokens(tokens);
 | 
			
		||||
        for (variant_index, variant) in variants.iter().enumerate() {
 | 
			
		||||
            let variant_var = format_ident!("__v_{}", variant.ident);
 | 
			
		||||
            let variant_name = &variant.ident;
 | 
			
		||||
            let variant_name_str = variant.ident.to_string();
 | 
			
		||||
            match_enum_variants.push(Variant {
 | 
			
		||||
                attrs: vec![],
 | 
			
		||||
                ident: variant.ident.clone(),
 | 
			
		||||
                fields: variant.fields_kind.into_fields(variant.fields.iter().map(
 | 
			
		||||
                    |ParsedField {
 | 
			
		||||
                         options,
 | 
			
		||||
                         vis,
 | 
			
		||||
                         name,
 | 
			
		||||
                         ty,
 | 
			
		||||
                     }| {
 | 
			
		||||
                        let FieldOptions {} = options.body;
 | 
			
		||||
                        Field {
 | 
			
		||||
                            attrs: vec![],
 | 
			
		||||
                            vis: vis.clone(),
 | 
			
		||||
                            mutability: FieldMutability::None,
 | 
			
		||||
                            ident: if let Member::Named(name) = name {
 | 
			
		||||
                                Some(name.clone())
 | 
			
		||||
                            } else {
 | 
			
		||||
                                None
 | 
			
		||||
                            },
 | 
			
		||||
                            colon_token: None,
 | 
			
		||||
                            ty: parse_quote! { ::fayalite::expr::Expr<#ty> },
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                )),
 | 
			
		||||
                discriminant: None,
 | 
			
		||||
            });
 | 
			
		||||
            let match_enum_field_names = Vec::from_iter(variant.fields.iter().map(
 | 
			
		||||
                |ParsedField {
 | 
			
		||||
                     options,
 | 
			
		||||
                     vis: _,
 | 
			
		||||
                     name,
 | 
			
		||||
                     ty: _,
 | 
			
		||||
                 }| {
 | 
			
		||||
                    let FieldOptions {} = options.body;
 | 
			
		||||
                    name
 | 
			
		||||
                },
 | 
			
		||||
            ));
 | 
			
		||||
            let match_enum_field_name_strs = Vec::from_iter(variant.fields.iter().map(
 | 
			
		||||
                |ParsedField {
 | 
			
		||||
                     options,
 | 
			
		||||
                     vis: _,
 | 
			
		||||
                     name,
 | 
			
		||||
                     ty: _,
 | 
			
		||||
                 }| {
 | 
			
		||||
                    let FieldOptions {} = options.body;
 | 
			
		||||
                    name.to_token_stream().to_string()
 | 
			
		||||
                },
 | 
			
		||||
            ));
 | 
			
		||||
            let match_enum_debug_vars = Vec::from_iter(variant.fields.iter().map(
 | 
			
		||||
                |ParsedField {
 | 
			
		||||
                     options,
 | 
			
		||||
                     vis: _,
 | 
			
		||||
                     name,
 | 
			
		||||
                     ty: _,
 | 
			
		||||
                 }| {
 | 
			
		||||
                    let FieldOptions {} = options.body;
 | 
			
		||||
                    format_ident!("__v_{}", name)
 | 
			
		||||
                },
 | 
			
		||||
            ));
 | 
			
		||||
            match_enum_debug_arms.push(match variant.fields_kind {
 | 
			
		||||
                FieldsKind::Unit | FieldsKind::Named(_) => quote! {
 | 
			
		||||
                    Self::#variant_name {
 | 
			
		||||
                        #(#match_enum_field_names: ref #match_enum_debug_vars,)*
 | 
			
		||||
                    } => f.debug_struct(#variant_name_str)
 | 
			
		||||
                        #(.field(#match_enum_field_name_strs, #match_enum_debug_vars))*
 | 
			
		||||
                        .finish(),
 | 
			
		||||
                },
 | 
			
		||||
                FieldsKind::Unnamed(_) => quote! {
 | 
			
		||||
                    Self::#variant_name(
 | 
			
		||||
                        #(ref #match_enum_debug_vars,)*
 | 
			
		||||
                    ) => f.debug_tuple(#variant_name_str)
 | 
			
		||||
                        #(.field(#match_enum_debug_vars))*
 | 
			
		||||
                        .finish(),
 | 
			
		||||
                },
 | 
			
		||||
            });
 | 
			
		||||
            if let Some(value_ty) = variant.value.value_ty() {
 | 
			
		||||
                from_canonical_type_variant_lets.push(quote! {
 | 
			
		||||
                    let #variant_var = #variant_var.from_canonical_type_helper_has_value(#variant_name_str);
 | 
			
		||||
                });
 | 
			
		||||
                non_empty_variant_vars.push(variant_var.clone());
 | 
			
		||||
                enum_type_variants.push(quote! {
 | 
			
		||||
                    ::fayalite::enum_::VariantType {
 | 
			
		||||
                        name: ::fayalite::intern::Intern::intern(#variant_name_str),
 | 
			
		||||
                        ty: ::fayalite::__std::option::Option::Some(
 | 
			
		||||
                            ::fayalite::ty::DynType::canonical_dyn(&self.#variant_name),
 | 
			
		||||
                        ),
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
                enum_type_variants_hint.push(quote! {
 | 
			
		||||
                    ::fayalite::enum_::VariantType {
 | 
			
		||||
                        name: ::fayalite::intern::Intern::intern(#variant_name_str),
 | 
			
		||||
                        ty: ::fayalite::__std::option::Option::Some(
 | 
			
		||||
                            ::fayalite::bundle::TypeHint::<<#value_ty as ::fayalite::expr::ToExpr>::Type>::intern_dyn(),
 | 
			
		||||
                        ),
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
            } else {
 | 
			
		||||
                from_canonical_type_variant_lets.push(quote! {
 | 
			
		||||
                    #variant_var.from_canonical_type_helper_no_value(#variant_name_str);
 | 
			
		||||
                });
 | 
			
		||||
                enum_type_variants.push(quote! {
 | 
			
		||||
                    ::fayalite::enum_::VariantType {
 | 
			
		||||
                        name: ::fayalite::intern::Intern::intern(#variant_name_str),
 | 
			
		||||
                        ty: ::fayalite::__std::option::Option::None,
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
                enum_type_variants_hint.push(quote! {
 | 
			
		||||
                    ::fayalite::enum_::VariantType {
 | 
			
		||||
                        name: ::fayalite::intern::Intern::intern(#variant_name_str),
 | 
			
		||||
                        ty: ::fayalite::__std::option::Option::None,
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
            variant_vars.push(variant_var);
 | 
			
		||||
            match_enum_arms.push(match &variant.value {
 | 
			
		||||
                VariantValue::None => quote! {
 | 
			
		||||
                    #variant_index => #match_enum_ident::#variant_name,
 | 
			
		||||
                },
 | 
			
		||||
                VariantValue::Direct { value_type } => quote! {
 | 
			
		||||
                    #variant_index => #match_enum_ident::#variant_name {
 | 
			
		||||
                        #(#match_enum_field_names)*: ::fayalite::expr::ToExpr::to_expr(
 | 
			
		||||
                            &__variant_access.downcast_unchecked::<
 | 
			
		||||
                                <#value_type as ::fayalite::expr::ToExpr>::Type>(),
 | 
			
		||||
                        ),
 | 
			
		||||
                    },
 | 
			
		||||
                },
 | 
			
		||||
                VariantValue::Struct {
 | 
			
		||||
                    value_struct: ItemStruct { ident, .. },
 | 
			
		||||
                    ..
 | 
			
		||||
                } => quote! {
 | 
			
		||||
                    #variant_index => {
 | 
			
		||||
                        let __variant_access = ::fayalite::expr::ToExpr::to_expr(
 | 
			
		||||
                            &__variant_access.downcast_unchecked::<
 | 
			
		||||
                                <#ident #fixed_type_type_generics as ::fayalite::expr::ToExpr>::Type,
 | 
			
		||||
                            >(),
 | 
			
		||||
                        );
 | 
			
		||||
                        #match_enum_ident::#variant_name {
 | 
			
		||||
                            #(#match_enum_field_names: (*__variant_access).#match_enum_field_names,)*
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                },
 | 
			
		||||
            });
 | 
			
		||||
            let builder_field_and_types = Vec::from_iter(variant.fields.iter().map(
 | 
			
		||||
                |ParsedField {
 | 
			
		||||
                     options,
 | 
			
		||||
                     vis: _,
 | 
			
		||||
                     name,
 | 
			
		||||
                     ty,
 | 
			
		||||
                 }| {
 | 
			
		||||
                    let FieldOptions {} = options.body;
 | 
			
		||||
                    (name, ty)
 | 
			
		||||
                },
 | 
			
		||||
            ));
 | 
			
		||||
            let builder_field_vars = Vec::from_iter(
 | 
			
		||||
                builder_field_and_types
 | 
			
		||||
                    .iter()
 | 
			
		||||
                    .map(|(name, _)| &builder.get_field(name).unwrap().1.builder_field_name),
 | 
			
		||||
            );
 | 
			
		||||
            let build_body = match &variant.value {
 | 
			
		||||
                VariantValue::None => parse_quote! {
 | 
			
		||||
                    {
 | 
			
		||||
                        ::fayalite::expr::ToExpr::to_expr(
 | 
			
		||||
                            &::fayalite::expr::ops::EnumLiteral::<#type_struct_ident #fixed_type_type_generics>::new_unchecked(
 | 
			
		||||
                                ::fayalite::__std::option::Option::None,
 | 
			
		||||
                                #variant_index,
 | 
			
		||||
                                ::fayalite::ty::FixedType::fixed_type(),
 | 
			
		||||
                            ),
 | 
			
		||||
                        )
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
                VariantValue::Direct { value_type: _ } => parse_quote! {
 | 
			
		||||
                    {
 | 
			
		||||
                        ::fayalite::expr::ToExpr::to_expr(
 | 
			
		||||
                            &::fayalite::expr::ops::EnumLiteral::<#type_struct_ident #fixed_type_type_generics>::new_unchecked(
 | 
			
		||||
                                ::fayalite::__std::option::Option::Some(#(#builder_field_vars)*.to_canonical_dyn()),
 | 
			
		||||
                                #variant_index,
 | 
			
		||||
                                ::fayalite::ty::FixedType::fixed_type(),
 | 
			
		||||
                            ),
 | 
			
		||||
                        )
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
                VariantValue::Struct {
 | 
			
		||||
                    parsed_struct:
 | 
			
		||||
                        ParsedStruct {
 | 
			
		||||
                            names:
 | 
			
		||||
                                ParsedStructNames {
 | 
			
		||||
                                    type_struct_ident: field_type_struct_ident,
 | 
			
		||||
                                    ..
 | 
			
		||||
                                },
 | 
			
		||||
                            ..
 | 
			
		||||
                        },
 | 
			
		||||
                    ..
 | 
			
		||||
                } => parse_quote! {
 | 
			
		||||
                    {
 | 
			
		||||
                        let __builder = <#field_type_struct_ident #fixed_type_type_generics as ::fayalite::bundle::BundleType>::builder();
 | 
			
		||||
                        #(let __builder = __builder.#builder_field_vars(#builder_field_vars);)*
 | 
			
		||||
                        ::fayalite::expr::ToExpr::to_expr(
 | 
			
		||||
                            &::fayalite::expr::ops::EnumLiteral::<#type_struct_ident #fixed_type_type_generics>::new_unchecked(
 | 
			
		||||
                                ::fayalite::__std::option::Option::Some(__builder.build().to_canonical_dyn()),
 | 
			
		||||
                                #variant_index,
 | 
			
		||||
                                ::fayalite::ty::FixedType::fixed_type(),
 | 
			
		||||
                            ),
 | 
			
		||||
                        )
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
            };
 | 
			
		||||
            builder
 | 
			
		||||
                .make_build_method(
 | 
			
		||||
                    &format_ident!("variant_{}", variant_name),
 | 
			
		||||
                    variant.fields.iter().map(
 | 
			
		||||
                        |ParsedField {
 | 
			
		||||
                             options,
 | 
			
		||||
                             vis: _,
 | 
			
		||||
                             name,
 | 
			
		||||
                             ty,
 | 
			
		||||
                         }| {
 | 
			
		||||
                            let FieldOptions {} = options.body;
 | 
			
		||||
                            (name.clone(), parse_quote! { ::fayalite::expr::Expr<#ty> })
 | 
			
		||||
                        },
 | 
			
		||||
                    ),
 | 
			
		||||
                    &fixed_type_generics,
 | 
			
		||||
                    &parse_quote! {#type_struct_ident #fixed_type_type_generics},
 | 
			
		||||
                    &parse_quote! { ::fayalite::expr::Expr<#target #fixed_type_type_generics> },
 | 
			
		||||
                    build_body,
 | 
			
		||||
                )
 | 
			
		||||
                .to_tokens(tokens);
 | 
			
		||||
        }
 | 
			
		||||
        let match_enum = ItemEnum {
 | 
			
		||||
            attrs: vec![parse_quote! {#[allow(non_camel_case_types)]}],
 | 
			
		||||
            vis: vis.clone(),
 | 
			
		||||
            enum_token: *enum_token,
 | 
			
		||||
            ident: match_enum_ident,
 | 
			
		||||
            generics: fixed_type_generics.clone(),
 | 
			
		||||
            brace_token: *brace_token,
 | 
			
		||||
            variants: match_enum_variants,
 | 
			
		||||
        };
 | 
			
		||||
        let match_enum_ident = &match_enum.ident;
 | 
			
		||||
        match_enum.to_tokens(tokens);
 | 
			
		||||
        make_connect_impl(
 | 
			
		||||
            *connect_inexact,
 | 
			
		||||
            &fixed_type_generics,
 | 
			
		||||
            type_struct_ident,
 | 
			
		||||
            variants.iter().flat_map(|variant| {
 | 
			
		||||
                variant.fields.iter().map(|field| {
 | 
			
		||||
                    let ty = &field.ty;
 | 
			
		||||
                    parse_quote_spanned! {field.name.span()=>
 | 
			
		||||
                        <#ty as ::fayalite::expr::ToExpr>::Type
 | 
			
		||||
                    }
 | 
			
		||||
                })
 | 
			
		||||
            }),
 | 
			
		||||
        )
 | 
			
		||||
        .to_tokens(tokens);
 | 
			
		||||
        let variant_count = variants.len();
 | 
			
		||||
        let empty_builder_ty = builder.ty([], Some(&parse_quote! { Self }), false);
 | 
			
		||||
        quote! {
 | 
			
		||||
            #[automatically_derived]
 | 
			
		||||
            impl #fixed_type_impl_generics ::fayalite::__std::fmt::Debug for #match_enum_ident #fixed_type_type_generics
 | 
			
		||||
            #fixed_type_where_clause
 | 
			
		||||
            {
 | 
			
		||||
                fn fmt(&self, f: &mut ::fayalite::__std::fmt::Formatter<'_>) -> ::fayalite::__std::fmt::Result {
 | 
			
		||||
                    match *self {
 | 
			
		||||
                        #(#match_enum_debug_arms)*
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            #[automatically_derived]
 | 
			
		||||
            impl #fixed_type_impl_generics ::fayalite::ty::FixedType for #type_struct_ident #fixed_type_type_generics
 | 
			
		||||
            #fixed_type_where_clause
 | 
			
		||||
            {
 | 
			
		||||
                fn fixed_type() -> Self {
 | 
			
		||||
                    Self {
 | 
			
		||||
                        #(#non_empty_variant_names: ::fayalite::ty::FixedType::fixed_type(),)*
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            fn __check_field<T: ::fayalite::ty::Value>()
 | 
			
		||||
            where
 | 
			
		||||
                <T as ::fayalite::expr::ToExpr>::Type: ::fayalite::ty::Type<Value = T>,
 | 
			
		||||
            {}
 | 
			
		||||
            fn __check_fields #fixed_type_impl_generics(_: #target #fixed_type_type_generics)
 | 
			
		||||
            #fixed_type_where_clause
 | 
			
		||||
            {
 | 
			
		||||
                #(#field_checks)*
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            #[automatically_derived]
 | 
			
		||||
            impl #fixed_type_impl_generics ::fayalite::__std::fmt::Debug for #type_struct_ident #fixed_type_type_generics
 | 
			
		||||
            #fixed_type_where_clause
 | 
			
		||||
            {
 | 
			
		||||
                fn fmt(&self, f: &mut ::fayalite::__std::fmt::Formatter<'_>) -> ::fayalite::__std::fmt::Result {
 | 
			
		||||
                    #debug_type_body
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            #[automatically_derived]
 | 
			
		||||
            impl #fixed_type_impl_generics ::fayalite::ty::Type for #type_struct_ident #fixed_type_type_generics
 | 
			
		||||
            #fixed_type_where_clause
 | 
			
		||||
            {
 | 
			
		||||
                type CanonicalType = ::fayalite::enum_::DynEnumType;
 | 
			
		||||
                type Value = #target #fixed_type_type_generics;
 | 
			
		||||
                type CanonicalValue = ::fayalite::enum_::DynEnum;
 | 
			
		||||
                type MaskType = ::fayalite::int::UIntType<1>;
 | 
			
		||||
                type MaskValue = ::fayalite::int::UInt<1>;
 | 
			
		||||
                type MatchVariant = #match_enum_ident #fixed_type_type_generics;
 | 
			
		||||
                type MatchActiveScope = ::fayalite::module::Scope;
 | 
			
		||||
                type MatchVariantAndInactiveScope = ::fayalite::enum_::EnumMatchVariantAndInactiveScope<Self>;
 | 
			
		||||
                type MatchVariantsIter = ::fayalite::enum_::EnumMatchVariantsIter<Self>;
 | 
			
		||||
                fn match_variants<IO: ::fayalite::bundle::BundleValue>(
 | 
			
		||||
                    this: ::fayalite::expr::Expr<<Self as ::fayalite::ty::Type>::Value>,
 | 
			
		||||
                    module_builder: &mut ::fayalite::module::ModuleBuilder<IO, ::fayalite::module::NormalModule>,
 | 
			
		||||
                    source_location: ::fayalite::source_location::SourceLocation,
 | 
			
		||||
                ) -> <Self as ::fayalite::ty::Type>::MatchVariantsIter
 | 
			
		||||
                where
 | 
			
		||||
                    <IO as ::fayalite::expr::ToExpr>::Type: ::fayalite::bundle::BundleType<Value = IO>,
 | 
			
		||||
                {
 | 
			
		||||
                    module_builder.enum_match_variants_helper(this, source_location)
 | 
			
		||||
                }
 | 
			
		||||
                fn mask_type(&self) -> <Self as ::fayalite::ty::Type>::MaskType {
 | 
			
		||||
                    ::fayalite::int::UIntType::new()
 | 
			
		||||
                }
 | 
			
		||||
                fn canonical(&self) -> <Self as ::fayalite::ty::Type>::CanonicalType {
 | 
			
		||||
                    let variants = ::fayalite::enum_::EnumType::variants(self);
 | 
			
		||||
                    ::fayalite::enum_::DynEnumType::new(variants)
 | 
			
		||||
                }
 | 
			
		||||
                fn source_location(&self) -> ::fayalite::source_location::SourceLocation {
 | 
			
		||||
                    ::fayalite::source_location::SourceLocation::caller()
 | 
			
		||||
                }
 | 
			
		||||
                fn type_enum(&self) -> ::fayalite::ty::TypeEnum {
 | 
			
		||||
                    ::fayalite::ty::TypeEnum::EnumType(::fayalite::ty::Type::canonical(self))
 | 
			
		||||
                }
 | 
			
		||||
                #[allow(non_snake_case)]
 | 
			
		||||
                fn from_canonical_type(t: <Self as ::fayalite::ty::Type>::CanonicalType) -> Self {
 | 
			
		||||
                    let [#(#variant_vars),*] = *::fayalite::enum_::EnumType::variants(&t) else {
 | 
			
		||||
                        ::fayalite::__std::panic!("wrong number of variants");
 | 
			
		||||
                    };
 | 
			
		||||
                    #(#from_canonical_type_variant_lets)*
 | 
			
		||||
                    Self {
 | 
			
		||||
                        #(#non_empty_variant_names: #non_empty_variant_vars,)*
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            #[automatically_derived]
 | 
			
		||||
            #[allow(clippy::init_numbered_fields)]
 | 
			
		||||
            impl #fixed_type_impl_generics ::fayalite::enum_::EnumType for #type_struct_ident #fixed_type_type_generics
 | 
			
		||||
            #fixed_type_where_clause
 | 
			
		||||
            {
 | 
			
		||||
                type Builder = #empty_builder_ty;
 | 
			
		||||
                fn match_activate_scope(
 | 
			
		||||
                    v: <Self as ::fayalite::ty::Type>::MatchVariantAndInactiveScope,
 | 
			
		||||
                ) -> (<Self as ::fayalite::ty::Type>::MatchVariant, <Self as ::fayalite::ty::Type>::MatchActiveScope) {
 | 
			
		||||
                    let (__variant_access, __scope) = v.activate();
 | 
			
		||||
                    (
 | 
			
		||||
                        match ::fayalite::expr::ops::VariantAccess::variant_index(&*__variant_access) {
 | 
			
		||||
                            #(#match_enum_arms)*
 | 
			
		||||
                            #variant_count.. => ::fayalite::__std::panic!("invalid variant index"),
 | 
			
		||||
                        },
 | 
			
		||||
                        __scope,
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
                fn builder() -> <Self as ::fayalite::enum_::EnumType>::Builder {
 | 
			
		||||
                    #empty_builder_ty::new()
 | 
			
		||||
                }
 | 
			
		||||
                fn variants(&self) -> ::fayalite::intern::Interned<[::fayalite::enum_::VariantType<::fayalite::intern::Interned<dyn ::fayalite::ty::DynCanonicalType>>]> {
 | 
			
		||||
                    ::fayalite::intern::Intern::intern(&[#(#enum_type_variants,)*][..])
 | 
			
		||||
                }
 | 
			
		||||
                fn variants_hint() -> ::fayalite::enum_::VariantsHint {
 | 
			
		||||
                    ::fayalite::enum_::VariantsHint::new([#(#enum_type_variants_hint,)*], false)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            #[automatically_derived]
 | 
			
		||||
            impl #fixed_type_impl_generics ::fayalite::expr::ToExpr for #target #fixed_type_type_generics
 | 
			
		||||
            #fixed_type_where_clause
 | 
			
		||||
            {
 | 
			
		||||
                type Type = #type_struct_ident #fixed_type_type_generics;
 | 
			
		||||
                fn ty(&self) -> <Self as ::fayalite::expr::ToExpr>::Type {
 | 
			
		||||
                    ::fayalite::ty::FixedType::fixed_type()
 | 
			
		||||
                }
 | 
			
		||||
                fn to_expr(&self) -> ::fayalite::expr::Expr<Self> {
 | 
			
		||||
                    ::fayalite::expr::Expr::from_value(self)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            #[automatically_derived]
 | 
			
		||||
            impl #fixed_type_impl_generics ::fayalite::ty::Value for #target #fixed_type_type_generics
 | 
			
		||||
            #fixed_type_where_clause
 | 
			
		||||
            {
 | 
			
		||||
                fn to_canonical(&self) -> <<Self as ::fayalite::expr::ToExpr>::Type as ::fayalite::ty::Type>::CanonicalValue {
 | 
			
		||||
                    let __ty = ::fayalite::ty::Type::canonical(&::fayalite::expr::ToExpr::ty(self));
 | 
			
		||||
                    match self {
 | 
			
		||||
                        #(Self::#variant_names { #variant_field_pats } => {
 | 
			
		||||
                            ::fayalite::enum_::DynEnum::new_by_name(
 | 
			
		||||
                                __ty,
 | 
			
		||||
                                ::fayalite::intern::Intern::intern(#variant_name_strs),
 | 
			
		||||
                                #variant_to_canonical_values,
 | 
			
		||||
                            )
 | 
			
		||||
                        })*
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            #[automatically_derived]
 | 
			
		||||
            impl #fixed_type_impl_generics ::fayalite::enum_::EnumValue for #target #fixed_type_type_generics
 | 
			
		||||
            #fixed_type_where_clause
 | 
			
		||||
            {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        .to_tokens(tokens);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) fn value_derive_enum(item: ItemEnum) -> syn::Result<TokenStream> {
 | 
			
		||||
    let item = ParsedEnum::parse(item)?;
 | 
			
		||||
    let outline_generated = item.options.body.outline_generated;
 | 
			
		||||
    let mut contents = quote! {
 | 
			
		||||
        const _: () = {
 | 
			
		||||
            #item
 | 
			
		||||
        };
 | 
			
		||||
    };
 | 
			
		||||
    if outline_generated.is_some() {
 | 
			
		||||
        contents = crate::outline_generated(contents, "value-enum-");
 | 
			
		||||
    }
 | 
			
		||||
    Ok(contents)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										709
									
								
								crates/fayalite-proc-macros-impl/src/value_derive_struct.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										709
									
								
								crates/fayalite-proc-macros-impl/src/value_derive_struct.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,709 @@
 | 
			
		|||
// SPDX-License-Identifier: LGPL-3.0-or-later
 | 
			
		||||
// See Notices.txt for copyright information
 | 
			
		||||
use crate::{
 | 
			
		||||
    value_derive_common::{
 | 
			
		||||
        append_field, derive_clone_hash_eq_partialeq_for_struct, get_target, make_connect_impl,
 | 
			
		||||
        Bounds, Builder, FieldsKind, ParsedField, ValueDeriveGenerics,
 | 
			
		||||
    },
 | 
			
		||||
    Errors, HdlAttr,
 | 
			
		||||
};
 | 
			
		||||
use proc_macro2::TokenStream;
 | 
			
		||||
use quote::{format_ident, quote, quote_spanned, ToTokens};
 | 
			
		||||
use syn::{
 | 
			
		||||
    parse_quote, parse_quote_spanned, spanned::Spanned, FieldMutability, Generics, Ident,
 | 
			
		||||
    ItemStruct, Member, Path, Token, Visibility,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
crate::options! {
 | 
			
		||||
    #[options = StructOptions]
 | 
			
		||||
    pub(crate) enum StructOption {
 | 
			
		||||
        OutlineGenerated(outline_generated),
 | 
			
		||||
        FixedType(fixed_type),
 | 
			
		||||
        ConnectInexact(connect_inexact),
 | 
			
		||||
        Bounds(where_, Bounds),
 | 
			
		||||
        Target(target, Path),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
crate::options! {
 | 
			
		||||
    #[options = FieldOptions]
 | 
			
		||||
    pub(crate) enum FieldOption {
 | 
			
		||||
        Flip(flip),
 | 
			
		||||
        Skip(skip),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) struct ParsedStructNames<I, S> {
 | 
			
		||||
    pub(crate) ident: Ident,
 | 
			
		||||
    pub(crate) type_struct_debug_ident: S,
 | 
			
		||||
    pub(crate) type_struct_ident: Ident,
 | 
			
		||||
    pub(crate) match_variant_ident: I,
 | 
			
		||||
    pub(crate) builder_struct_ident: I,
 | 
			
		||||
    pub(crate) mask_match_variant_ident: I,
 | 
			
		||||
    pub(crate) mask_type_ident: I,
 | 
			
		||||
    pub(crate) mask_type_debug_ident: S,
 | 
			
		||||
    pub(crate) mask_value_ident: I,
 | 
			
		||||
    pub(crate) mask_value_debug_ident: S,
 | 
			
		||||
    pub(crate) mask_builder_struct_ident: I,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) struct ParsedStruct {
 | 
			
		||||
    pub(crate) options: HdlAttr<StructOptions>,
 | 
			
		||||
    pub(crate) vis: Visibility,
 | 
			
		||||
    pub(crate) struct_token: Token![struct],
 | 
			
		||||
    pub(crate) generics: Generics,
 | 
			
		||||
    pub(crate) fields_kind: FieldsKind,
 | 
			
		||||
    pub(crate) fields: Vec<ParsedField<FieldOptions>>,
 | 
			
		||||
    pub(crate) semi_token: Option<Token![;]>,
 | 
			
		||||
    pub(crate) skip_check_fields: bool,
 | 
			
		||||
    pub(crate) names: ParsedStructNames<Option<Ident>, Option<String>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ParsedStruct {
 | 
			
		||||
    pub(crate) fn parse(item: &mut ItemStruct) -> syn::Result<Self> {
 | 
			
		||||
        let ItemStruct {
 | 
			
		||||
            attrs,
 | 
			
		||||
            vis,
 | 
			
		||||
            struct_token,
 | 
			
		||||
            ident,
 | 
			
		||||
            generics,
 | 
			
		||||
            fields,
 | 
			
		||||
            semi_token,
 | 
			
		||||
        } = item;
 | 
			
		||||
        let mut errors = Errors::new();
 | 
			
		||||
        let struct_options = errors
 | 
			
		||||
            .unwrap_or_default(HdlAttr::parse_and_take_attr(attrs))
 | 
			
		||||
            .unwrap_or_default();
 | 
			
		||||
        let (fields_kind, fields) = ParsedField::parse_fields(&mut errors, fields, false);
 | 
			
		||||
        errors.finish()?;
 | 
			
		||||
        Ok(ParsedStruct {
 | 
			
		||||
            options: struct_options,
 | 
			
		||||
            vis: vis.clone(),
 | 
			
		||||
            struct_token: *struct_token,
 | 
			
		||||
            generics: generics.clone(),
 | 
			
		||||
            fields_kind,
 | 
			
		||||
            fields,
 | 
			
		||||
            semi_token: *semi_token,
 | 
			
		||||
            skip_check_fields: false,
 | 
			
		||||
            names: ParsedStructNames {
 | 
			
		||||
                ident: ident.clone(),
 | 
			
		||||
                type_struct_debug_ident: None,
 | 
			
		||||
                type_struct_ident: format_ident!("__{}__Type", ident),
 | 
			
		||||
                match_variant_ident: None,
 | 
			
		||||
                builder_struct_ident: None,
 | 
			
		||||
                mask_match_variant_ident: None,
 | 
			
		||||
                mask_type_ident: None,
 | 
			
		||||
                mask_type_debug_ident: None,
 | 
			
		||||
                mask_value_ident: None,
 | 
			
		||||
                mask_value_debug_ident: None,
 | 
			
		||||
                mask_builder_struct_ident: None,
 | 
			
		||||
            },
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
    pub(crate) fn write_body(
 | 
			
		||||
        &self,
 | 
			
		||||
        target: Path,
 | 
			
		||||
        names: ParsedStructNames<&Ident, &String>,
 | 
			
		||||
        is_for_mask: bool,
 | 
			
		||||
        tokens: &mut TokenStream,
 | 
			
		||||
    ) {
 | 
			
		||||
        let Self {
 | 
			
		||||
            options,
 | 
			
		||||
            vis,
 | 
			
		||||
            struct_token,
 | 
			
		||||
            generics,
 | 
			
		||||
            fields_kind,
 | 
			
		||||
            fields,
 | 
			
		||||
            semi_token,
 | 
			
		||||
            skip_check_fields,
 | 
			
		||||
            names: _,
 | 
			
		||||
        } = self;
 | 
			
		||||
        let skip_check_fields = *skip_check_fields || is_for_mask;
 | 
			
		||||
        let ParsedStructNames {
 | 
			
		||||
            ident: struct_ident,
 | 
			
		||||
            type_struct_debug_ident,
 | 
			
		||||
            type_struct_ident,
 | 
			
		||||
            match_variant_ident,
 | 
			
		||||
            builder_struct_ident,
 | 
			
		||||
            mask_match_variant_ident: _,
 | 
			
		||||
            mask_type_ident,
 | 
			
		||||
            mask_type_debug_ident: _,
 | 
			
		||||
            mask_value_ident,
 | 
			
		||||
            mask_value_debug_ident,
 | 
			
		||||
            mask_builder_struct_ident: _,
 | 
			
		||||
        } = names;
 | 
			
		||||
        let StructOptions {
 | 
			
		||||
            outline_generated: _,
 | 
			
		||||
            where_,
 | 
			
		||||
            target: _,
 | 
			
		||||
            fixed_type,
 | 
			
		||||
            connect_inexact,
 | 
			
		||||
        } = &options.body;
 | 
			
		||||
        let ValueDeriveGenerics {
 | 
			
		||||
            generics,
 | 
			
		||||
            fixed_type_generics,
 | 
			
		||||
        } = ValueDeriveGenerics::get(generics.clone(), where_);
 | 
			
		||||
        let (impl_generics, type_generics, where_clause) = generics.split_for_impl();
 | 
			
		||||
        let unskipped_fields = fields
 | 
			
		||||
            .iter()
 | 
			
		||||
            .filter(|field| field.options.body.skip.is_none());
 | 
			
		||||
        let _field_names = Vec::from_iter(fields.iter().map(|field| field.name.clone()));
 | 
			
		||||
        let unskipped_field_names =
 | 
			
		||||
            Vec::from_iter(unskipped_fields.clone().map(|field| field.name.clone()));
 | 
			
		||||
        let unskipped_field_name_strs = Vec::from_iter(
 | 
			
		||||
            unskipped_field_names
 | 
			
		||||
                .iter()
 | 
			
		||||
                .map(|field_name| field_name.to_token_stream().to_string()),
 | 
			
		||||
        );
 | 
			
		||||
        let unskipped_field_vars = Vec::from_iter(
 | 
			
		||||
            unskipped_field_names
 | 
			
		||||
                .iter()
 | 
			
		||||
                .map(|field_name| format_ident!("__v_{}", field_name)),
 | 
			
		||||
        );
 | 
			
		||||
        let unskipped_field_flips = Vec::from_iter(
 | 
			
		||||
            unskipped_fields
 | 
			
		||||
                .clone()
 | 
			
		||||
                .map(|field| field.options.body.flip.is_some()),
 | 
			
		||||
        );
 | 
			
		||||
        let mut any_fields_skipped = false;
 | 
			
		||||
        let type_fields = Vec::from_iter(fields.iter().filter_map(|field| {
 | 
			
		||||
            let ParsedField {
 | 
			
		||||
                options,
 | 
			
		||||
                vis,
 | 
			
		||||
                name,
 | 
			
		||||
                ty,
 | 
			
		||||
            } = field;
 | 
			
		||||
            let FieldOptions { flip: _, skip } = &options.body;
 | 
			
		||||
            if skip.is_some() {
 | 
			
		||||
                any_fields_skipped = true;
 | 
			
		||||
                return None;
 | 
			
		||||
            }
 | 
			
		||||
            let ty = if is_for_mask {
 | 
			
		||||
                parse_quote! { ::fayalite::ty::AsMask<#ty> }
 | 
			
		||||
            } else {
 | 
			
		||||
                ty.to_token_stream()
 | 
			
		||||
            };
 | 
			
		||||
            Some(syn::Field {
 | 
			
		||||
                attrs: vec![],
 | 
			
		||||
                vis: vis.clone(),
 | 
			
		||||
                mutability: FieldMutability::None,
 | 
			
		||||
                ident: match name.clone() {
 | 
			
		||||
                    Member::Named(name) => Some(name),
 | 
			
		||||
                    Member::Unnamed(_) => None,
 | 
			
		||||
                },
 | 
			
		||||
                colon_token: None,
 | 
			
		||||
                ty: parse_quote! { <#ty as ::fayalite::expr::ToExpr>::Type },
 | 
			
		||||
            })
 | 
			
		||||
        }));
 | 
			
		||||
        let field_types = Vec::from_iter(type_fields.iter().map(|field| field.ty.clone()));
 | 
			
		||||
        let match_variant_fields = Vec::from_iter(fields.iter().zip(&type_fields).map(
 | 
			
		||||
            |(parsed_field, type_field)| {
 | 
			
		||||
                let field_ty = &parsed_field.ty;
 | 
			
		||||
                syn::Field {
 | 
			
		||||
                    ty: parse_quote! { ::fayalite::expr::Expr<#field_ty> },
 | 
			
		||||
                    ..type_field.clone()
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
        ));
 | 
			
		||||
 | 
			
		||||
        let mask_value_fields = Vec::from_iter(fields.iter().zip(&type_fields).map(
 | 
			
		||||
            |(parsed_field, type_field)| {
 | 
			
		||||
                let field_ty = &parsed_field.ty;
 | 
			
		||||
                syn::Field {
 | 
			
		||||
                    ty: parse_quote! { ::fayalite::ty::AsMask<#field_ty> },
 | 
			
		||||
                    ..type_field.clone()
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
        ));
 | 
			
		||||
 | 
			
		||||
        let mut type_struct_fields = fields_kind.into_fields(type_fields);
 | 
			
		||||
        let mut match_variant_fields = fields_kind.into_fields(match_variant_fields);
 | 
			
		||||
        let mut mask_value_fields = fields_kind.into_fields(mask_value_fields);
 | 
			
		||||
        let phantom_data_field_name = any_fields_skipped.then(|| {
 | 
			
		||||
            let phantom_data_field_name = Ident::new("__phantom_data", type_struct_ident.span());
 | 
			
		||||
            let member = append_field(
 | 
			
		||||
                &mut type_struct_fields,
 | 
			
		||||
                syn::Field {
 | 
			
		||||
                    attrs: vec![],
 | 
			
		||||
                    vis: vis.clone(),
 | 
			
		||||
                    mutability: FieldMutability::None,
 | 
			
		||||
                    ident: Some(phantom_data_field_name.clone()),
 | 
			
		||||
                    colon_token: None,
 | 
			
		||||
                    ty: parse_quote_spanned! {type_struct_ident.span()=>
 | 
			
		||||
                        ::fayalite::__std::marker::PhantomData<#struct_ident #type_generics>
 | 
			
		||||
                    },
 | 
			
		||||
                },
 | 
			
		||||
            );
 | 
			
		||||
            append_field(
 | 
			
		||||
                &mut match_variant_fields,
 | 
			
		||||
                syn::Field {
 | 
			
		||||
                    attrs: vec![],
 | 
			
		||||
                    vis: Visibility::Inherited,
 | 
			
		||||
                    mutability: FieldMutability::None,
 | 
			
		||||
                    ident: Some(phantom_data_field_name.clone()),
 | 
			
		||||
                    colon_token: None,
 | 
			
		||||
                    ty: parse_quote_spanned! {type_struct_ident.span()=>
 | 
			
		||||
                        ::fayalite::__std::marker::PhantomData<#struct_ident #type_generics>
 | 
			
		||||
                    },
 | 
			
		||||
                },
 | 
			
		||||
            );
 | 
			
		||||
            append_field(
 | 
			
		||||
                &mut mask_value_fields,
 | 
			
		||||
                syn::Field {
 | 
			
		||||
                    attrs: vec![],
 | 
			
		||||
                    vis: Visibility::Inherited,
 | 
			
		||||
                    mutability: FieldMutability::None,
 | 
			
		||||
                    ident: Some(phantom_data_field_name),
 | 
			
		||||
                    colon_token: None,
 | 
			
		||||
                    ty: parse_quote_spanned! {type_struct_ident.span()=>
 | 
			
		||||
                        ::fayalite::__std::marker::PhantomData<#struct_ident #type_generics>
 | 
			
		||||
                    },
 | 
			
		||||
                },
 | 
			
		||||
            );
 | 
			
		||||
            member
 | 
			
		||||
        });
 | 
			
		||||
        let phantom_data_field_name_slice = phantom_data_field_name.as_slice();
 | 
			
		||||
        let type_struct = ItemStruct {
 | 
			
		||||
            attrs: vec![parse_quote! {#[allow(non_camel_case_types)]}],
 | 
			
		||||
            vis: vis.clone(),
 | 
			
		||||
            struct_token: *struct_token,
 | 
			
		||||
            ident: type_struct_ident.clone(),
 | 
			
		||||
            generics: generics.clone(),
 | 
			
		||||
            fields: type_struct_fields,
 | 
			
		||||
            semi_token: *semi_token,
 | 
			
		||||
        };
 | 
			
		||||
        type_struct.to_tokens(tokens);
 | 
			
		||||
        let match_variant_struct = ItemStruct {
 | 
			
		||||
            attrs: vec![parse_quote! {#[allow(non_camel_case_types)]}],
 | 
			
		||||
            vis: vis.clone(),
 | 
			
		||||
            struct_token: *struct_token,
 | 
			
		||||
            ident: match_variant_ident.clone(),
 | 
			
		||||
            generics: generics.clone(),
 | 
			
		||||
            fields: match_variant_fields,
 | 
			
		||||
            semi_token: *semi_token,
 | 
			
		||||
        };
 | 
			
		||||
        match_variant_struct.to_tokens(tokens);
 | 
			
		||||
        let mask_type_body = if is_for_mask {
 | 
			
		||||
            quote! {
 | 
			
		||||
                ::fayalite::__std::clone::Clone::clone(self)
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            let mask_value_struct = ItemStruct {
 | 
			
		||||
                attrs: vec![parse_quote! {#[allow(non_camel_case_types)]}],
 | 
			
		||||
                vis: vis.clone(),
 | 
			
		||||
                struct_token: *struct_token,
 | 
			
		||||
                ident: mask_value_ident.clone(),
 | 
			
		||||
                generics: generics.clone(),
 | 
			
		||||
                fields: mask_value_fields,
 | 
			
		||||
                semi_token: *semi_token,
 | 
			
		||||
            };
 | 
			
		||||
            mask_value_struct.to_tokens(tokens);
 | 
			
		||||
            let debug_mask_value_body = match fields_kind {
 | 
			
		||||
                FieldsKind::Unit => quote! {
 | 
			
		||||
                    f.debug_struct(#mask_value_debug_ident).finish()
 | 
			
		||||
                },
 | 
			
		||||
                FieldsKind::Named(_) => quote! {
 | 
			
		||||
                    f.debug_struct(#mask_value_debug_ident)#(.field(#unskipped_field_name_strs, &self.#unskipped_field_names))*.finish()
 | 
			
		||||
                },
 | 
			
		||||
                FieldsKind::Unnamed(_) => quote! {
 | 
			
		||||
                    f.debug_tuple(#mask_value_debug_ident)#(.field(&self.#unskipped_field_names))*.finish()
 | 
			
		||||
                },
 | 
			
		||||
            };
 | 
			
		||||
            quote! {
 | 
			
		||||
                #[automatically_derived]
 | 
			
		||||
                impl #impl_generics ::fayalite::__std::fmt::Debug for #mask_value_ident #type_generics
 | 
			
		||||
                #where_clause
 | 
			
		||||
                {
 | 
			
		||||
                    fn fmt(&self, f: &mut ::fayalite::__std::fmt::Formatter<'_>) -> ::fayalite::__std::fmt::Result {
 | 
			
		||||
                        #debug_mask_value_body
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }.to_tokens(tokens);
 | 
			
		||||
            quote! {
 | 
			
		||||
                #mask_type_ident {
 | 
			
		||||
                    #(#unskipped_field_names: ::fayalite::ty::Type::mask_type(&self.#unskipped_field_names),)*
 | 
			
		||||
                    #(#phantom_data_field_name_slice: ::fayalite::__std::marker::PhantomData,)*
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
        let debug_type_body = match fields_kind {
 | 
			
		||||
            FieldsKind::Unit => quote! {
 | 
			
		||||
                f.debug_struct(#type_struct_debug_ident).finish()
 | 
			
		||||
            },
 | 
			
		||||
            FieldsKind::Named(_) => quote! {
 | 
			
		||||
                f.debug_struct(#type_struct_debug_ident)#(.field(#unskipped_field_name_strs, &self.#unskipped_field_names))*.finish()
 | 
			
		||||
            },
 | 
			
		||||
            FieldsKind::Unnamed(_) => quote! {
 | 
			
		||||
                f.debug_tuple(#type_struct_debug_ident)#(.field(&self.#unskipped_field_names))*.finish()
 | 
			
		||||
            },
 | 
			
		||||
        };
 | 
			
		||||
        for the_struct_ident in [&type_struct_ident, match_variant_ident]
 | 
			
		||||
            .into_iter()
 | 
			
		||||
            .chain(is_for_mask.then_some(mask_value_ident))
 | 
			
		||||
        {
 | 
			
		||||
            derive_clone_hash_eq_partialeq_for_struct(
 | 
			
		||||
                the_struct_ident,
 | 
			
		||||
                &generics,
 | 
			
		||||
                &Vec::from_iter(
 | 
			
		||||
                    unskipped_field_names
 | 
			
		||||
                        .iter()
 | 
			
		||||
                        .cloned()
 | 
			
		||||
                        .chain(phantom_data_field_name.clone()),
 | 
			
		||||
                ),
 | 
			
		||||
            )
 | 
			
		||||
            .to_tokens(tokens);
 | 
			
		||||
        }
 | 
			
		||||
        let check_v = format_ident!("__v");
 | 
			
		||||
        let field_checks = Vec::from_iter(fields.iter().map(|ParsedField { ty, name, .. }| {
 | 
			
		||||
            quote_spanned! {ty.span()=>
 | 
			
		||||
                __check_field(#check_v.#name);
 | 
			
		||||
            }
 | 
			
		||||
        }));
 | 
			
		||||
        if fixed_type.is_some() {
 | 
			
		||||
            let (impl_generics, type_generics, where_clause) = fixed_type_generics.split_for_impl();
 | 
			
		||||
            quote! {
 | 
			
		||||
                #[automatically_derived]
 | 
			
		||||
                impl #impl_generics ::fayalite::ty::FixedType for #type_struct_ident #type_generics
 | 
			
		||||
                #where_clause
 | 
			
		||||
                {
 | 
			
		||||
                    fn fixed_type() -> Self {
 | 
			
		||||
                        Self {
 | 
			
		||||
                            #(#unskipped_field_names: ::fayalite::ty::FixedType::fixed_type(),)*
 | 
			
		||||
                            #(#phantom_data_field_name_slice: ::fayalite::__std::marker::PhantomData,)*
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            .to_tokens(tokens);
 | 
			
		||||
        }
 | 
			
		||||
        if !skip_check_fields {
 | 
			
		||||
            quote! {
 | 
			
		||||
                fn __check_field<T: ::fayalite::ty::Value>(_v: T)
 | 
			
		||||
                where
 | 
			
		||||
                    <T as ::fayalite::expr::ToExpr>::Type: ::fayalite::ty::Type<Value = T>,
 | 
			
		||||
                {}
 | 
			
		||||
                fn __check_fields #impl_generics(#check_v: #target #type_generics)
 | 
			
		||||
                #where_clause
 | 
			
		||||
                {
 | 
			
		||||
                    #(#field_checks)*
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            .to_tokens(tokens);
 | 
			
		||||
        }
 | 
			
		||||
        let mut builder = Builder::new(builder_struct_ident.clone(), vis.clone());
 | 
			
		||||
        for field in unskipped_fields.clone() {
 | 
			
		||||
            builder.insert_field(
 | 
			
		||||
                    field.name.clone(),
 | 
			
		||||
                    |v| {
 | 
			
		||||
                        parse_quote_spanned! {v.span()=>
 | 
			
		||||
                            ::fayalite::expr::ToExpr::to_expr(&#v)
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                    |t| {
 | 
			
		||||
                        parse_quote_spanned! {t.span()=>
 | 
			
		||||
                            ::fayalite::expr::Expr<<<#t as ::fayalite::expr::ToExpr>::Type as ::fayalite::ty::Type>::Value>
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                    |t| {
 | 
			
		||||
                        parse_quote_spanned! {t.span()=>
 | 
			
		||||
                            where
 | 
			
		||||
                                #t: ::fayalite::expr::ToExpr,
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                );
 | 
			
		||||
        }
 | 
			
		||||
        let builder = builder.finish_filling_in_fields();
 | 
			
		||||
        builder.to_tokens(tokens);
 | 
			
		||||
        let build_type_fields =
 | 
			
		||||
            Vec::from_iter(unskipped_fields.clone().map(|ParsedField { name, .. }| {
 | 
			
		||||
                let builder_field_name = &builder.get_field(name).unwrap().1.builder_field_name;
 | 
			
		||||
                quote_spanned! {struct_ident.span()=>
 | 
			
		||||
                    #name: ::fayalite::expr::ToExpr::ty(&#builder_field_name)
 | 
			
		||||
                }
 | 
			
		||||
            }));
 | 
			
		||||
        let build_expr_fields =
 | 
			
		||||
            Vec::from_iter(unskipped_fields.clone().map(|ParsedField { name, .. }| {
 | 
			
		||||
                let builder_field_name = &builder.get_field(name).unwrap().1.builder_field_name;
 | 
			
		||||
                quote_spanned! {struct_ident.span()=>
 | 
			
		||||
                    #builder_field_name.to_canonical_dyn()
 | 
			
		||||
                }
 | 
			
		||||
            }));
 | 
			
		||||
        let build_specified_fields = unskipped_fields.clone().map(
 | 
			
		||||
            |ParsedField {
 | 
			
		||||
                 options: _,
 | 
			
		||||
                 vis: _,
 | 
			
		||||
                 name,
 | 
			
		||||
                 ty,
 | 
			
		||||
             }| {
 | 
			
		||||
                let ty = if is_for_mask {
 | 
			
		||||
                    parse_quote_spanned! {name.span()=>
 | 
			
		||||
                        ::fayalite::expr::Expr<::fayalite::ty::AsMask<#ty>>
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    parse_quote_spanned! {name.span()=>
 | 
			
		||||
                        ::fayalite::expr::Expr<#ty>
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
                (name.clone(), ty)
 | 
			
		||||
            },
 | 
			
		||||
        );
 | 
			
		||||
        let build_body = parse_quote_spanned! {struct_ident.span()=>
 | 
			
		||||
            {
 | 
			
		||||
                ::fayalite::expr::ToExpr::to_expr(
 | 
			
		||||
                    &::fayalite::expr::ops::BundleLiteral::new_unchecked(
 | 
			
		||||
                        ::fayalite::intern::Intern::intern(&[#(
 | 
			
		||||
                            #build_expr_fields,
 | 
			
		||||
                        )*][..]),
 | 
			
		||||
                        #type_struct_ident {
 | 
			
		||||
                            #(#build_type_fields,)*
 | 
			
		||||
                            #(#phantom_data_field_name_slice: ::fayalite::__std::marker::PhantomData,)*
 | 
			
		||||
                        },
 | 
			
		||||
                    ),
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
        builder
 | 
			
		||||
            .make_build_method(
 | 
			
		||||
                &Ident::new("build", struct_ident.span()),
 | 
			
		||||
                build_specified_fields,
 | 
			
		||||
                &generics,
 | 
			
		||||
                &parse_quote_spanned! {struct_ident.span()=>
 | 
			
		||||
                    #type_struct_ident #type_generics
 | 
			
		||||
                },
 | 
			
		||||
                &parse_quote_spanned! {struct_ident.span()=>
 | 
			
		||||
                    ::fayalite::expr::Expr<#target #type_generics>
 | 
			
		||||
                },
 | 
			
		||||
                build_body,
 | 
			
		||||
            )
 | 
			
		||||
            .to_tokens(tokens);
 | 
			
		||||
        make_connect_impl(
 | 
			
		||||
            *connect_inexact,
 | 
			
		||||
            &generics,
 | 
			
		||||
            &type_struct_ident,
 | 
			
		||||
            unskipped_fields.clone().map(|field| {
 | 
			
		||||
                let ty = &field.ty;
 | 
			
		||||
                parse_quote_spanned! {field.name.span()=>
 | 
			
		||||
                    <#ty as ::fayalite::expr::ToExpr>::Type
 | 
			
		||||
                }
 | 
			
		||||
            }),
 | 
			
		||||
        )
 | 
			
		||||
        .to_tokens(tokens);
 | 
			
		||||
        let empty_builder_ty = builder.ty([], Some(&parse_quote! { Self }), false);
 | 
			
		||||
        quote! {
 | 
			
		||||
            #[automatically_derived]
 | 
			
		||||
            impl #impl_generics ::fayalite::__std::fmt::Debug for #type_struct_ident #type_generics
 | 
			
		||||
            #where_clause
 | 
			
		||||
            {
 | 
			
		||||
                fn fmt(&self, f: &mut ::fayalite::__std::fmt::Formatter<'_>) -> ::fayalite::__std::fmt::Result {
 | 
			
		||||
                    #debug_type_body
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            #[automatically_derived]
 | 
			
		||||
            impl #impl_generics ::fayalite::ty::Type for #type_struct_ident #type_generics
 | 
			
		||||
            #where_clause
 | 
			
		||||
            {
 | 
			
		||||
                type CanonicalType = ::fayalite::bundle::DynBundleType;
 | 
			
		||||
                type Value = #target #type_generics;
 | 
			
		||||
                type CanonicalValue = ::fayalite::bundle::DynBundle;
 | 
			
		||||
                type MaskType = #mask_type_ident #type_generics;
 | 
			
		||||
                type MaskValue = #mask_value_ident #type_generics;
 | 
			
		||||
                type MatchVariant = #match_variant_ident #type_generics;
 | 
			
		||||
                type MatchActiveScope = ();
 | 
			
		||||
                type MatchVariantAndInactiveScope = ::fayalite::ty::MatchVariantWithoutScope<#match_variant_ident #type_generics>;
 | 
			
		||||
                type MatchVariantsIter = ::fayalite::__std::iter::Once<<Self as ::fayalite::ty::Type>::MatchVariantAndInactiveScope>;
 | 
			
		||||
                #[allow(unused_variables)]
 | 
			
		||||
                fn match_variants<IO: ::fayalite::bundle::BundleValue>(
 | 
			
		||||
                    this: ::fayalite::expr::Expr<<Self as ::fayalite::ty::Type>::Value>,
 | 
			
		||||
                    module_builder: &mut ::fayalite::module::ModuleBuilder<IO, ::fayalite::module::NormalModule>,
 | 
			
		||||
                    source_location: ::fayalite::source_location::SourceLocation,
 | 
			
		||||
                ) -> <Self as ::fayalite::ty::Type>::MatchVariantsIter
 | 
			
		||||
                where
 | 
			
		||||
                    <IO as ::fayalite::expr::ToExpr>::Type: ::fayalite::bundle::BundleType<Value = IO>,
 | 
			
		||||
                {
 | 
			
		||||
                    ::fayalite::__std::iter::once(::fayalite::ty::MatchVariantWithoutScope(#match_variant_ident {
 | 
			
		||||
                        #(#unskipped_field_names: this.field(#unskipped_field_name_strs),)*
 | 
			
		||||
                        #(#phantom_data_field_name_slice: ::fayalite::__std::marker::PhantomData,)*
 | 
			
		||||
                    }))
 | 
			
		||||
                }
 | 
			
		||||
                fn mask_type(&self) -> <Self as ::fayalite::ty::Type>::MaskType {
 | 
			
		||||
                    #mask_type_body
 | 
			
		||||
                }
 | 
			
		||||
                fn canonical(&self) -> <Self as ::fayalite::ty::Type>::CanonicalType {
 | 
			
		||||
                    let fields = ::fayalite::bundle::BundleType::fields(self);
 | 
			
		||||
                    ::fayalite::bundle::DynBundleType::new(fields)
 | 
			
		||||
                }
 | 
			
		||||
                fn source_location(&self) -> ::fayalite::source_location::SourceLocation {
 | 
			
		||||
                    ::fayalite::source_location::SourceLocation::caller()
 | 
			
		||||
                }
 | 
			
		||||
                fn type_enum(&self) -> ::fayalite::ty::TypeEnum {
 | 
			
		||||
                    ::fayalite::ty::TypeEnum::BundleType(::fayalite::ty::Type::canonical(self))
 | 
			
		||||
                }
 | 
			
		||||
                fn from_canonical_type(t: <Self as ::fayalite::ty::Type>::CanonicalType) -> Self {
 | 
			
		||||
                    let [#(#unskipped_field_vars),*] = *::fayalite::bundle::BundleType::fields(&t) else {
 | 
			
		||||
                        ::fayalite::__std::panic!("wrong number of fields");
 | 
			
		||||
                    };
 | 
			
		||||
                    Self {
 | 
			
		||||
                        #(#unskipped_field_names: #unskipped_field_vars.from_canonical_type_helper(#unskipped_field_name_strs, #unskipped_field_flips),)*
 | 
			
		||||
                        #(#phantom_data_field_name_slice: ::fayalite::__std::marker::PhantomData,)*
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            #[automatically_derived]
 | 
			
		||||
            impl #impl_generics ::fayalite::ty::TypeWithDeref for #type_struct_ident #type_generics
 | 
			
		||||
            #where_clause
 | 
			
		||||
            {
 | 
			
		||||
                #[allow(unused_variables)]
 | 
			
		||||
                fn expr_deref(this: &::fayalite::expr::Expr<<Self as ::fayalite::ty::Type>::Value>) -> &<Self as ::fayalite::ty::Type>::MatchVariant {
 | 
			
		||||
                    ::fayalite::intern::Interned::<_>::into_inner(::fayalite::intern::Intern::intern_sized(
 | 
			
		||||
                        #match_variant_ident {
 | 
			
		||||
                            #(#unskipped_field_names: this.field(#unskipped_field_name_strs),)*
 | 
			
		||||
                            #(#phantom_data_field_name_slice: ::fayalite::__std::marker::PhantomData,)*
 | 
			
		||||
                        }
 | 
			
		||||
                    ))
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            #[automatically_derived]
 | 
			
		||||
            impl #impl_generics ::fayalite::bundle::BundleType for #type_struct_ident #type_generics
 | 
			
		||||
            #where_clause
 | 
			
		||||
            {
 | 
			
		||||
                type Builder = #empty_builder_ty;
 | 
			
		||||
                fn builder() -> <Self as ::fayalite::bundle::BundleType>::Builder {
 | 
			
		||||
                    #empty_builder_ty::new()
 | 
			
		||||
                }
 | 
			
		||||
                fn fields(&self) -> ::fayalite::intern::Interned<[::fayalite::bundle::FieldType<::fayalite::intern::Interned<dyn ::fayalite::ty::DynCanonicalType>>]> {
 | 
			
		||||
                    ::fayalite::intern::Intern::intern(&[#(
 | 
			
		||||
                        ::fayalite::bundle::FieldType {
 | 
			
		||||
                            name: ::fayalite::intern::Intern::intern(#unskipped_field_name_strs),
 | 
			
		||||
                            flipped: #unskipped_field_flips,
 | 
			
		||||
                            ty: ::fayalite::ty::DynType::canonical_dyn(&self.#unskipped_field_names),
 | 
			
		||||
                        },
 | 
			
		||||
                    )*][..])
 | 
			
		||||
                }
 | 
			
		||||
                fn fields_hint() -> ::fayalite::bundle::FieldsHint {
 | 
			
		||||
                    ::fayalite::bundle::FieldsHint::new([#(
 | 
			
		||||
                        ::fayalite::bundle::FieldType {
 | 
			
		||||
                            name: ::fayalite::intern::Intern::intern(#unskipped_field_name_strs),
 | 
			
		||||
                            flipped: #unskipped_field_flips,
 | 
			
		||||
                            ty: ::fayalite::bundle::TypeHint::<#field_types>::intern_dyn(),
 | 
			
		||||
                        },
 | 
			
		||||
                    )*], false)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            #[automatically_derived]
 | 
			
		||||
            impl #impl_generics ::fayalite::expr::ToExpr for #target #type_generics
 | 
			
		||||
            #where_clause
 | 
			
		||||
            {
 | 
			
		||||
                type Type = #type_struct_ident #type_generics;
 | 
			
		||||
                fn ty(&self) -> <Self as ::fayalite::expr::ToExpr>::Type {
 | 
			
		||||
                    #type_struct_ident {
 | 
			
		||||
                        #(#unskipped_field_names: ::fayalite::expr::ToExpr::ty(&self.#unskipped_field_names),)*
 | 
			
		||||
                        #(#phantom_data_field_name_slice: ::fayalite::__std::marker::PhantomData,)*
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                fn to_expr(&self) -> ::fayalite::expr::Expr<Self> {
 | 
			
		||||
                    ::fayalite::expr::Expr::from_value(self)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            #[automatically_derived]
 | 
			
		||||
            impl #impl_generics ::fayalite::ty::Value for #target #type_generics
 | 
			
		||||
            #where_clause
 | 
			
		||||
            {
 | 
			
		||||
                fn to_canonical(&self) -> <<Self as ::fayalite::expr::ToExpr>::Type as ::fayalite::ty::Type>::CanonicalValue {
 | 
			
		||||
                    let ty = ::fayalite::ty::Type::canonical(&::fayalite::expr::ToExpr::ty(self));
 | 
			
		||||
                    ::fayalite::bundle::DynBundle::new(ty, ::fayalite::__std::sync::Arc::new([
 | 
			
		||||
                        #(::fayalite::ty::DynValueTrait::to_canonical_dyn(&self.#unskipped_field_names),)*
 | 
			
		||||
                    ]))
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            #[automatically_derived]
 | 
			
		||||
            impl #impl_generics ::fayalite::bundle::BundleValue for #target #type_generics
 | 
			
		||||
            #where_clause
 | 
			
		||||
            {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        .to_tokens(tokens);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ToTokens for ParsedStruct {
 | 
			
		||||
    fn to_tokens(&self, tokens: &mut TokenStream) {
 | 
			
		||||
        let ParsedStructNames {
 | 
			
		||||
            ident: struct_ident,
 | 
			
		||||
            type_struct_debug_ident,
 | 
			
		||||
            type_struct_ident,
 | 
			
		||||
            match_variant_ident,
 | 
			
		||||
            builder_struct_ident,
 | 
			
		||||
            mask_match_variant_ident,
 | 
			
		||||
            mask_type_ident,
 | 
			
		||||
            mask_type_debug_ident,
 | 
			
		||||
            mask_value_ident,
 | 
			
		||||
            mask_value_debug_ident,
 | 
			
		||||
            mask_builder_struct_ident,
 | 
			
		||||
        } = &self.names;
 | 
			
		||||
        macro_rules! unwrap_or_set {
 | 
			
		||||
            ($(let $var:ident =? $fallback_value:expr;)*) => {
 | 
			
		||||
                $(let $var = $var.clone().unwrap_or_else(|| $fallback_value);)*
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
        unwrap_or_set! {
 | 
			
		||||
            let type_struct_debug_ident =? format!("{struct_ident}::Type");
 | 
			
		||||
            let match_variant_ident =? format_ident!("__{}__MatchVariant", struct_ident);
 | 
			
		||||
            let builder_struct_ident =? format_ident!("__{}__Builder", struct_ident);
 | 
			
		||||
            let mask_match_variant_ident =? format_ident!("__AsMask__{}__MatchVariant", struct_ident);
 | 
			
		||||
            let mask_type_ident =? format_ident!("__AsMask__{}__Type", struct_ident);
 | 
			
		||||
            let mask_type_debug_ident =? format!("AsMask<{struct_ident}>::Type");
 | 
			
		||||
            let mask_value_ident =? format_ident!("__AsMask__{}", struct_ident);
 | 
			
		||||
            let mask_value_debug_ident =? format!("AsMask<{struct_ident}>");
 | 
			
		||||
            let mask_builder_struct_ident =? format_ident!("__AsMask__{}__Builder", struct_ident);
 | 
			
		||||
        }
 | 
			
		||||
        let target = get_target(&self.options.body.target, struct_ident);
 | 
			
		||||
        let names = ParsedStructNames {
 | 
			
		||||
            ident: struct_ident.clone(),
 | 
			
		||||
            type_struct_debug_ident: &type_struct_debug_ident,
 | 
			
		||||
            type_struct_ident: type_struct_ident.clone(),
 | 
			
		||||
            match_variant_ident: &match_variant_ident,
 | 
			
		||||
            builder_struct_ident: &builder_struct_ident,
 | 
			
		||||
            mask_match_variant_ident: &mask_match_variant_ident,
 | 
			
		||||
            mask_type_ident: &mask_type_ident,
 | 
			
		||||
            mask_type_debug_ident: &mask_type_debug_ident,
 | 
			
		||||
            mask_value_ident: &mask_value_ident,
 | 
			
		||||
            mask_value_debug_ident: &mask_value_debug_ident,
 | 
			
		||||
            mask_builder_struct_ident: &mask_builder_struct_ident,
 | 
			
		||||
        };
 | 
			
		||||
        self.write_body(target, names, false, tokens);
 | 
			
		||||
        let mask_names = ParsedStructNames {
 | 
			
		||||
            ident: mask_value_ident.clone(),
 | 
			
		||||
            type_struct_debug_ident: &mask_type_debug_ident,
 | 
			
		||||
            type_struct_ident: mask_type_ident.clone(),
 | 
			
		||||
            match_variant_ident: &mask_match_variant_ident,
 | 
			
		||||
            builder_struct_ident: &mask_builder_struct_ident,
 | 
			
		||||
            mask_match_variant_ident: &mask_match_variant_ident,
 | 
			
		||||
            mask_type_ident: &mask_type_ident,
 | 
			
		||||
            mask_type_debug_ident: &mask_type_debug_ident,
 | 
			
		||||
            mask_value_ident: &mask_value_ident,
 | 
			
		||||
            mask_value_debug_ident: &mask_value_debug_ident,
 | 
			
		||||
            mask_builder_struct_ident: &mask_builder_struct_ident,
 | 
			
		||||
        };
 | 
			
		||||
        self.write_body(mask_value_ident.clone().into(), mask_names, true, tokens);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) fn value_derive_struct(mut item: ItemStruct) -> syn::Result<TokenStream> {
 | 
			
		||||
    let item = ParsedStruct::parse(&mut item)?;
 | 
			
		||||
    let outline_generated = item.options.body.outline_generated;
 | 
			
		||||
    let mut contents = quote! {
 | 
			
		||||
        const _: () = {
 | 
			
		||||
            #item
 | 
			
		||||
        };
 | 
			
		||||
    };
 | 
			
		||||
    if outline_generated.is_some() {
 | 
			
		||||
        contents = crate::outline_generated(contents, "value-struct-");
 | 
			
		||||
    }
 | 
			
		||||
    Ok(contents)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										14
									
								
								crates/fayalite-proc-macros/Cargo.toml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								crates/fayalite-proc-macros/Cargo.toml
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,14 @@
 | 
			
		|||
# SPDX-License-Identifier: LGPL-3.0-or-later
 | 
			
		||||
# See Notices.txt for copyright information
 | 
			
		||||
[package]
 | 
			
		||||
name = "fayalite-proc-macros"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
edition = "2021"
 | 
			
		||||
workspace = "../.."
 | 
			
		||||
license = "LGPL-3.0-or-later"
 | 
			
		||||
 | 
			
		||||
[lib]
 | 
			
		||||
proc-macro = true
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
fayalite-proc-macros-impl = { version = "=0.1.0", path = "../fayalite-proc-macros-impl" }
 | 
			
		||||
							
								
								
									
										20
									
								
								crates/fayalite-proc-macros/src/lib.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								crates/fayalite-proc-macros/src/lib.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,20 @@
 | 
			
		|||
// SPDX-License-Identifier: LGPL-3.0-or-later
 | 
			
		||||
// See Notices.txt for copyright information
 | 
			
		||||
#[proc_macro_attribute]
 | 
			
		||||
pub fn hdl_module(
 | 
			
		||||
    attr: proc_macro::TokenStream,
 | 
			
		||||
    item: proc_macro::TokenStream,
 | 
			
		||||
) -> proc_macro::TokenStream {
 | 
			
		||||
    match fayalite_proc_macros_impl::module(attr.into(), item.into()) {
 | 
			
		||||
        Ok(retval) => retval.into(),
 | 
			
		||||
        Err(err) => err.into_compile_error().into(),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[proc_macro_derive(Value, attributes(hdl))]
 | 
			
		||||
pub fn value_derive(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
 | 
			
		||||
    match fayalite_proc_macros_impl::value_derive(item.into()) {
 | 
			
		||||
        Ok(retval) => retval.into(),
 | 
			
		||||
        Err(err) => err.into_compile_error().into(),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										18
									
								
								crates/fayalite-visit-gen/Cargo.toml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								crates/fayalite-visit-gen/Cargo.toml
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,18 @@
 | 
			
		|||
# SPDX-License-Identifier: LGPL-3.0-or-later
 | 
			
		||||
# See Notices.txt for copyright information
 | 
			
		||||
[package]
 | 
			
		||||
name = "fayalite-visit-gen"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
edition = "2021"
 | 
			
		||||
workspace = "../.."
 | 
			
		||||
license = "LGPL-3.0-or-later"
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
indexmap = { version = "2.2.6", features = ["serde"] }
 | 
			
		||||
prettyplease = "0.2.20"
 | 
			
		||||
proc-macro2 = "1.0.83"
 | 
			
		||||
quote = "1.0.36"
 | 
			
		||||
serde = { version = "1.0.202", features = ["derive"] }
 | 
			
		||||
serde_json = { version = "1.0.117", features = ["preserve_order"] }
 | 
			
		||||
syn = { version = "2.0.66", features = ["full", "extra-traits"] }
 | 
			
		||||
thiserror = "1.0.61"
 | 
			
		||||
							
								
								
									
										613
									
								
								crates/fayalite-visit-gen/src/ast.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										613
									
								
								crates/fayalite-visit-gen/src/ast.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,613 @@
 | 
			
		|||
// SPDX-License-Identifier: LGPL-3.0-or-later
 | 
			
		||||
// See Notices.txt for copyright information
 | 
			
		||||
use indexmap::IndexMap;
 | 
			
		||||
use proc_macro2::{Span, TokenStream};
 | 
			
		||||
use quote::{IdentFragment, ToTokens, TokenStreamExt};
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
use std::{
 | 
			
		||||
    fmt::{self, Write},
 | 
			
		||||
    iter::FusedIterator,
 | 
			
		||||
    str::FromStr,
 | 
			
		||||
};
 | 
			
		||||
use thiserror::Error;
 | 
			
		||||
 | 
			
		||||
macro_rules! impl_try_from_str {
 | 
			
		||||
    ($ty:ty) => {
 | 
			
		||||
        impl TryFrom<&'_ str> for $ty {
 | 
			
		||||
            type Error = <Self as FromStr>::Err;
 | 
			
		||||
 | 
			
		||||
            fn try_from(v: &str) -> Result<Self, Self::Error> {
 | 
			
		||||
                v.parse()
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        impl TryFrom<String> for $ty {
 | 
			
		||||
            type Error = <Self as FromStr>::Err;
 | 
			
		||||
 | 
			
		||||
            fn try_from(v: String) -> Result<Self, Self::Error> {
 | 
			
		||||
                v.parse()
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Hash)]
 | 
			
		||||
#[serde(into = "String", try_from = "String")]
 | 
			
		||||
pub struct Ident(pub String);
 | 
			
		||||
 | 
			
		||||
impl ToTokens for Ident {
 | 
			
		||||
    fn to_tokens(&self, tokens: &mut TokenStream) {
 | 
			
		||||
        syn::Ident::from(self).to_tokens(tokens);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl IdentFragment for Ident {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 | 
			
		||||
        f.write_str(&self.0)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Ident {
 | 
			
		||||
    pub fn is_start_char(ch: char) -> bool {
 | 
			
		||||
        ch == '_' || ch.is_ascii_alphabetic()
 | 
			
		||||
    }
 | 
			
		||||
    pub fn is_continue_char(ch: char) -> bool {
 | 
			
		||||
        ch == '_' || ch.is_ascii_alphanumeric()
 | 
			
		||||
    }
 | 
			
		||||
    pub fn is_ident(v: &str) -> bool {
 | 
			
		||||
        !v.is_empty()
 | 
			
		||||
            && v.starts_with(Self::is_start_char)
 | 
			
		||||
            && v.trim_start_matches(Self::is_continue_char).is_empty()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<Ident> for Path {
 | 
			
		||||
    fn from(value: Ident) -> Self {
 | 
			
		||||
        Path(value.0)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<Ident> for String {
 | 
			
		||||
    fn from(value: Ident) -> Self {
 | 
			
		||||
        value.0
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<Ident> for syn::Ident {
 | 
			
		||||
    fn from(value: Ident) -> Self {
 | 
			
		||||
        From::from(&value)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<&'_ Ident> for syn::Ident {
 | 
			
		||||
    fn from(value: &Ident) -> Self {
 | 
			
		||||
        syn::Ident::new(&value.0, Span::call_site())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug, Error)]
 | 
			
		||||
#[error("invalid identifier")]
 | 
			
		||||
pub struct IdentParseError;
 | 
			
		||||
 | 
			
		||||
impl_try_from_str!(Ident);
 | 
			
		||||
 | 
			
		||||
impl FromStr for Ident {
 | 
			
		||||
    type Err = IdentParseError;
 | 
			
		||||
 | 
			
		||||
    fn from_str(s: &str) -> Result<Self, Self::Err> {
 | 
			
		||||
        if Self::is_ident(s) {
 | 
			
		||||
            Ok(Self(s.into()))
 | 
			
		||||
        } else {
 | 
			
		||||
            Err(IdentParseError)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Hash)]
 | 
			
		||||
#[serde(into = "String", try_from = "String")]
 | 
			
		||||
 | 
			
		||||
pub struct Path(String);
 | 
			
		||||
 | 
			
		||||
impl Path {
 | 
			
		||||
    pub fn iter(&self) -> PathIter<'_> {
 | 
			
		||||
        PathIter(&self.0)
 | 
			
		||||
    }
 | 
			
		||||
    pub fn last(&self) -> Ident {
 | 
			
		||||
        self.iter().next_back().unwrap()
 | 
			
		||||
    }
 | 
			
		||||
    pub fn is_path(s: &str) -> bool {
 | 
			
		||||
        if s.is_empty() {
 | 
			
		||||
            false
 | 
			
		||||
        } else {
 | 
			
		||||
            s.split("::").all(Ident::is_ident)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
pub struct PathIter<'a>(&'a str);
 | 
			
		||||
 | 
			
		||||
impl Iterator for PathIter<'_> {
 | 
			
		||||
    type Item = Ident;
 | 
			
		||||
 | 
			
		||||
    fn next(&mut self) -> Option<Self::Item> {
 | 
			
		||||
        if self.0.is_empty() {
 | 
			
		||||
            None
 | 
			
		||||
        } else if let Some((first, rest)) = self.0.split_once("::") {
 | 
			
		||||
            self.0 = rest;
 | 
			
		||||
            Some(Ident(first.into()))
 | 
			
		||||
        } else {
 | 
			
		||||
            let retval = self.0;
 | 
			
		||||
            self.0 = &self.0[..0];
 | 
			
		||||
            Some(Ident(retval.into()))
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn last(mut self) -> Option<Self::Item> {
 | 
			
		||||
        self.next_back()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl FusedIterator for PathIter<'_> {}
 | 
			
		||||
 | 
			
		||||
impl DoubleEndedIterator for PathIter<'_> {
 | 
			
		||||
    fn next_back(&mut self) -> Option<Self::Item> {
 | 
			
		||||
        if self.0.is_empty() {
 | 
			
		||||
            None
 | 
			
		||||
        } else if let Some((rest, last)) = self.0.rsplit_once("::") {
 | 
			
		||||
            self.0 = rest;
 | 
			
		||||
            Some(Ident(last.into()))
 | 
			
		||||
        } else {
 | 
			
		||||
            let retval = self.0;
 | 
			
		||||
            self.0 = &self.0[..0];
 | 
			
		||||
            Some(Ident(retval.into()))
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ToTokens for Path {
 | 
			
		||||
    fn to_tokens(&self, tokens: &mut TokenStream) {
 | 
			
		||||
        tokens.append_separated(self.iter(), <syn::Token![::]>::default());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug, Error)]
 | 
			
		||||
#[error("invalid path")]
 | 
			
		||||
pub struct PathParseError;
 | 
			
		||||
 | 
			
		||||
impl From<Path> for String {
 | 
			
		||||
    fn from(value: Path) -> Self {
 | 
			
		||||
        value.0
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl_try_from_str!(Path);
 | 
			
		||||
 | 
			
		||||
impl FromStr for Path {
 | 
			
		||||
    type Err = PathParseError;
 | 
			
		||||
 | 
			
		||||
    fn from_str(value: &str) -> Result<Self, Self::Err> {
 | 
			
		||||
        if value.is_empty() {
 | 
			
		||||
            Err(PathParseError)
 | 
			
		||||
        } else if value.split("::").all(Ident::is_ident) {
 | 
			
		||||
            Ok(Self(value.into()))
 | 
			
		||||
        } else {
 | 
			
		||||
            Err(PathParseError)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
 | 
			
		||||
pub struct Definitions {
 | 
			
		||||
    pub types: std::collections::BTreeMap<Path, Definition>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
 | 
			
		||||
pub struct Definition {
 | 
			
		||||
    #[serde(default, skip_serializing_if = "Option::is_none")]
 | 
			
		||||
    pub fn_name_suffix: Option<Ident>,
 | 
			
		||||
    #[serde(default, skip_serializing_if = "Option::is_none")]
 | 
			
		||||
    pub generics: Option<Generics>,
 | 
			
		||||
    #[serde(default, skip_serializing_if = "Option::is_none")]
 | 
			
		||||
    pub fold_where: Option<WherePredicates>,
 | 
			
		||||
    #[serde(default, skip_serializing_if = "Option::is_none")]
 | 
			
		||||
    pub visit_where: Option<WherePredicates>,
 | 
			
		||||
    pub data: Data,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
 | 
			
		||||
#[serde(tag = "$kind")]
 | 
			
		||||
pub enum Data {
 | 
			
		||||
    ManualImpl,
 | 
			
		||||
    Opaque,
 | 
			
		||||
    Enum(Variants),
 | 
			
		||||
    Struct(Fields),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Hash)]
 | 
			
		||||
#[serde(into = "String", try_from = "String")]
 | 
			
		||||
pub struct FieldNameIdent {
 | 
			
		||||
    pub ident: Ident,
 | 
			
		||||
    pub is_getter: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl FieldNameIdent {
 | 
			
		||||
    pub fn to_member(&self) -> Option<syn::Member> {
 | 
			
		||||
        let Self {
 | 
			
		||||
            ref ident,
 | 
			
		||||
            is_getter,
 | 
			
		||||
        } = *self;
 | 
			
		||||
        if is_getter {
 | 
			
		||||
            None
 | 
			
		||||
        } else {
 | 
			
		||||
            Some(syn::Ident::from(ident).into())
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ToTokens for FieldNameIdent {
 | 
			
		||||
    fn to_tokens(&self, tokens: &mut TokenStream) {
 | 
			
		||||
        let Self {
 | 
			
		||||
            ref ident,
 | 
			
		||||
            is_getter,
 | 
			
		||||
        } = *self;
 | 
			
		||||
        ident.to_tokens(tokens);
 | 
			
		||||
        if is_getter {
 | 
			
		||||
            syn::token::Paren::default().surround(tokens, |_| {});
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<FieldNameIdent> for String {
 | 
			
		||||
    fn from(value: FieldNameIdent) -> Self {
 | 
			
		||||
        let mut retval = value.ident.0;
 | 
			
		||||
        if value.is_getter {
 | 
			
		||||
            retval.push_str("()");
 | 
			
		||||
        }
 | 
			
		||||
        retval
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug, Error)]
 | 
			
		||||
#[error("invalid field name")]
 | 
			
		||||
pub struct FieldNameParseError;
 | 
			
		||||
 | 
			
		||||
impl_try_from_str!(FieldNameIdent);
 | 
			
		||||
 | 
			
		||||
impl FromStr for FieldNameIdent {
 | 
			
		||||
    type Err = FieldNameParseError;
 | 
			
		||||
 | 
			
		||||
    fn from_str(value: &str) -> Result<Self, Self::Err> {
 | 
			
		||||
        let ident = value.strip_suffix("()");
 | 
			
		||||
        let is_getter = ident.is_some();
 | 
			
		||||
        let ident = ident.unwrap_or(value);
 | 
			
		||||
        if let Ok(ident) = ident.parse() {
 | 
			
		||||
            Ok(Self { ident, is_getter })
 | 
			
		||||
        } else {
 | 
			
		||||
            Err(FieldNameParseError)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default, Hash)]
 | 
			
		||||
#[serde(into = "String", try_from = "String")]
 | 
			
		||||
pub struct WherePredicates(pub syn::punctuated::Punctuated<syn::WherePredicate, syn::Token![,]>);
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Error)]
 | 
			
		||||
#[error("invalid `where` predicates")]
 | 
			
		||||
pub struct WherePredicatesParseError;
 | 
			
		||||
 | 
			
		||||
impl_try_from_str!(WherePredicates);
 | 
			
		||||
 | 
			
		||||
impl FromStr for WherePredicates {
 | 
			
		||||
    type Err = WherePredicatesParseError;
 | 
			
		||||
 | 
			
		||||
    fn from_str(value: &str) -> Result<Self, Self::Err> {
 | 
			
		||||
        Ok(Self(
 | 
			
		||||
            syn::parse::Parser::parse_str(syn::punctuated::Punctuated::parse_terminated, value)
 | 
			
		||||
                .map_err(|_| WherePredicatesParseError)?,
 | 
			
		||||
        ))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<WherePredicates> for String {
 | 
			
		||||
    fn from(value: WherePredicates) -> Self {
 | 
			
		||||
        value.0.into_token_stream().to_string()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<WherePredicates> for syn::WhereClause {
 | 
			
		||||
    fn from(value: WherePredicates) -> Self {
 | 
			
		||||
        syn::WhereClause {
 | 
			
		||||
            where_token: Default::default(),
 | 
			
		||||
            predicates: value.0,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<syn::WhereClause> for WherePredicates {
 | 
			
		||||
    fn from(value: syn::WhereClause) -> Self {
 | 
			
		||||
        Self(value.predicates)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ToTokens for WherePredicates {
 | 
			
		||||
    fn to_tokens(&self, tokens: &mut TokenStream) {
 | 
			
		||||
        self.0.to_tokens(tokens);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Serialize, Deserialize)]
 | 
			
		||||
#[serde(untagged)]
 | 
			
		||||
enum SerializedGenerics {
 | 
			
		||||
    Where {
 | 
			
		||||
        generics: String,
 | 
			
		||||
        #[serde(rename = "where")]
 | 
			
		||||
        where_predicates: WherePredicates,
 | 
			
		||||
    },
 | 
			
		||||
    NoWhere(String),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default, Hash)]
 | 
			
		||||
#[serde(into = "SerializedGenerics", try_from = "SerializedGenerics")]
 | 
			
		||||
pub struct Generics(pub syn::Generics);
 | 
			
		||||
 | 
			
		||||
impl ToTokens for Generics {
 | 
			
		||||
    fn to_tokens(&self, tokens: &mut TokenStream) {
 | 
			
		||||
        self.0.to_tokens(tokens);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<Generics> for SerializedGenerics {
 | 
			
		||||
    fn from(mut value: Generics) -> Self {
 | 
			
		||||
        match value.0.where_clause.take() {
 | 
			
		||||
            Some(where_clause) => Self::Where {
 | 
			
		||||
                generics: value.0.into_token_stream().to_string(),
 | 
			
		||||
                where_predicates: where_clause.into(),
 | 
			
		||||
            },
 | 
			
		||||
            None => Self::NoWhere(value.0.into_token_stream().to_string()),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Error)]
 | 
			
		||||
#[error("invalid generics")]
 | 
			
		||||
pub struct GenericsParseError;
 | 
			
		||||
 | 
			
		||||
impl TryFrom<SerializedGenerics> for Generics {
 | 
			
		||||
    type Error = GenericsParseError;
 | 
			
		||||
 | 
			
		||||
    fn try_from(value: SerializedGenerics) -> Result<Self, Self::Error> {
 | 
			
		||||
        let (generics, where_clause) = match value {
 | 
			
		||||
            SerializedGenerics::Where {
 | 
			
		||||
                generics,
 | 
			
		||||
                where_predicates,
 | 
			
		||||
            } => (generics, Some(where_predicates.into())),
 | 
			
		||||
            SerializedGenerics::NoWhere(generics) => (generics, None),
 | 
			
		||||
        };
 | 
			
		||||
        let Ok(mut generics) = syn::parse_str::<syn::Generics>(&generics) else {
 | 
			
		||||
            return Err(GenericsParseError);
 | 
			
		||||
        };
 | 
			
		||||
        generics.where_clause = where_clause;
 | 
			
		||||
        Ok(Self(generics))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Hash)]
 | 
			
		||||
#[serde(into = "String", try_from = "String")]
 | 
			
		||||
pub struct PathWithGenerics {
 | 
			
		||||
    pub path: Path,
 | 
			
		||||
    pub generics: Option<syn::AngleBracketedGenericArguments>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ToTokens for PathWithGenerics {
 | 
			
		||||
    fn to_tokens(&self, tokens: &mut TokenStream) {
 | 
			
		||||
        let Self { path, generics } = self;
 | 
			
		||||
        path.to_tokens(tokens);
 | 
			
		||||
        if let Some(generics) = generics {
 | 
			
		||||
            <syn::Token![::]>::default().to_tokens(tokens);
 | 
			
		||||
            generics.to_tokens(tokens);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<PathWithGenerics> for String {
 | 
			
		||||
    fn from(value: PathWithGenerics) -> Self {
 | 
			
		||||
        let PathWithGenerics { path, generics } = value;
 | 
			
		||||
        let mut retval = String::from(path);
 | 
			
		||||
        if let Some(generics) = generics {
 | 
			
		||||
            write!(retval, "{}", generics.to_token_stream()).unwrap();
 | 
			
		||||
        }
 | 
			
		||||
        retval
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug, Error)]
 | 
			
		||||
#[error("invalid path with optional generics")]
 | 
			
		||||
pub struct PathWithGenericsParseError;
 | 
			
		||||
 | 
			
		||||
impl_try_from_str!(PathWithGenerics);
 | 
			
		||||
 | 
			
		||||
impl FromStr for PathWithGenerics {
 | 
			
		||||
    type Err = PathWithGenericsParseError;
 | 
			
		||||
 | 
			
		||||
    fn from_str(value: &str) -> Result<Self, Self::Err> {
 | 
			
		||||
        let (path, generics) = if let Some(lt_pos) = value.find('<') {
 | 
			
		||||
            let (path, generics) = value.split_at(lt_pos);
 | 
			
		||||
            let path = path.strip_suffix("::").unwrap_or(path);
 | 
			
		||||
            match syn::parse_str(generics) {
 | 
			
		||||
                Ok(generics) => (path, Some(generics)),
 | 
			
		||||
                Err(_) => return Err(PathWithGenericsParseError),
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            (value, None)
 | 
			
		||||
        };
 | 
			
		||||
        if let Ok(path) = path.parse() {
 | 
			
		||||
            Ok(Self { path, generics })
 | 
			
		||||
        } else {
 | 
			
		||||
            Err(PathWithGenericsParseError)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Hash)]
 | 
			
		||||
#[serde(into = "String", try_from = "String")]
 | 
			
		||||
pub enum FieldName {
 | 
			
		||||
    Index(usize),
 | 
			
		||||
    Ident(FieldNameIdent),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl FieldName {
 | 
			
		||||
    pub fn to_member(&self) -> Option<syn::Member> {
 | 
			
		||||
        match self {
 | 
			
		||||
            &FieldName::Index(index) => Some(index.into()),
 | 
			
		||||
            FieldName::Ident(ident) => ident.to_member(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ToTokens for FieldName {
 | 
			
		||||
    fn to_tokens(&self, tokens: &mut TokenStream) {
 | 
			
		||||
        match self {
 | 
			
		||||
            &FieldName::Index(index) => syn::Index::from(index).to_tokens(tokens),
 | 
			
		||||
            FieldName::Ident(ident) => ident.to_tokens(tokens),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<FieldName> for String {
 | 
			
		||||
    fn from(value: FieldName) -> Self {
 | 
			
		||||
        match value {
 | 
			
		||||
            FieldName::Index(index) => index.to_string(),
 | 
			
		||||
            FieldName::Ident(ident) => ident.into(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl_try_from_str!(FieldName);
 | 
			
		||||
 | 
			
		||||
impl FromStr for FieldName {
 | 
			
		||||
    type Err = FieldNameParseError;
 | 
			
		||||
 | 
			
		||||
    fn from_str(value: &str) -> Result<Self, Self::Err> {
 | 
			
		||||
        if !value.is_empty()
 | 
			
		||||
            && value
 | 
			
		||||
                .trim_start_matches(|ch: char| ch.is_ascii_digit())
 | 
			
		||||
                .is_empty()
 | 
			
		||||
        {
 | 
			
		||||
            if let Ok(index) = value.parse() {
 | 
			
		||||
                Ok(Self::Index(index))
 | 
			
		||||
            } else {
 | 
			
		||||
                Err(FieldNameParseError)
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            value.parse().map(Self::Ident)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
 | 
			
		||||
pub struct Fields {
 | 
			
		||||
    #[serde(
 | 
			
		||||
        default,
 | 
			
		||||
        rename = "$constructor",
 | 
			
		||||
        skip_serializing_if = "Option::is_none"
 | 
			
		||||
    )]
 | 
			
		||||
    pub constructor: Option<PathWithGenerics>,
 | 
			
		||||
    #[serde(flatten)]
 | 
			
		||||
    pub fields: IndexMap<FieldName, Field>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
 | 
			
		||||
#[serde(transparent)]
 | 
			
		||||
pub struct Variants {
 | 
			
		||||
    pub variants: IndexMap<Ident, Option<Field>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Hash)]
 | 
			
		||||
pub enum Field {
 | 
			
		||||
    Opaque,
 | 
			
		||||
    Visible,
 | 
			
		||||
    RefVisible,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use crate::ast;
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_serialize() {
 | 
			
		||||
        let definitions = ast::Definitions {
 | 
			
		||||
            types: FromIterator::from_iter([
 | 
			
		||||
                (
 | 
			
		||||
                    ast::Path("Module".into()),
 | 
			
		||||
                    ast::Definition {
 | 
			
		||||
                        fn_name_suffix: None,
 | 
			
		||||
                        generics: Some(
 | 
			
		||||
                            ast::SerializedGenerics::Where {
 | 
			
		||||
                                generics: "<T: BundleValue>".into(),
 | 
			
		||||
                                where_predicates: "T::Type: BundleType<Value = T>,"
 | 
			
		||||
                                    .parse()
 | 
			
		||||
                                    .unwrap(),
 | 
			
		||||
                            }
 | 
			
		||||
                            .try_into()
 | 
			
		||||
                            .unwrap(),
 | 
			
		||||
                        ),
 | 
			
		||||
                        fold_where: None,
 | 
			
		||||
                        visit_where: None,
 | 
			
		||||
                        data: ast::Data::Struct(ast::Fields {
 | 
			
		||||
                            constructor: Some("Module::new_unchecked".parse().unwrap()),
 | 
			
		||||
                            fields: FromIterator::from_iter([(
 | 
			
		||||
                                "name_id()".parse().unwrap(),
 | 
			
		||||
                                ast::Field::Visible,
 | 
			
		||||
                            )]),
 | 
			
		||||
                        }),
 | 
			
		||||
                    },
 | 
			
		||||
                ),
 | 
			
		||||
                (
 | 
			
		||||
                    ast::Path("NameId".into()),
 | 
			
		||||
                    ast::Definition {
 | 
			
		||||
                        fn_name_suffix: None,
 | 
			
		||||
                        generics: None,
 | 
			
		||||
                        fold_where: None,
 | 
			
		||||
                        visit_where: None,
 | 
			
		||||
                        data: ast::Data::Struct(ast::Fields {
 | 
			
		||||
                            constructor: None,
 | 
			
		||||
                            fields: FromIterator::from_iter([
 | 
			
		||||
                                ("0".try_into().unwrap(), ast::Field::Opaque),
 | 
			
		||||
                                ("1".try_into().unwrap(), ast::Field::Opaque),
 | 
			
		||||
                            ]),
 | 
			
		||||
                        }),
 | 
			
		||||
                    },
 | 
			
		||||
                ),
 | 
			
		||||
            ]),
 | 
			
		||||
        };
 | 
			
		||||
        let definitions_str = serde_json::to_string_pretty(&definitions).unwrap();
 | 
			
		||||
        println!("{definitions_str}");
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            definitions_str,
 | 
			
		||||
            r#"{
 | 
			
		||||
  "types": {
 | 
			
		||||
    "Module": {
 | 
			
		||||
      "generics": {
 | 
			
		||||
        "generics": "< T : BundleValue >",
 | 
			
		||||
        "where": "T :: Type : BundleType < Value = T > ,"
 | 
			
		||||
      },
 | 
			
		||||
      "data": {
 | 
			
		||||
        "$kind": "Struct",
 | 
			
		||||
        "$constructor": "Module::new_unchecked",
 | 
			
		||||
        "name_id()": "Visible"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "NameId": {
 | 
			
		||||
      "data": {
 | 
			
		||||
        "$kind": "Struct",
 | 
			
		||||
        "0": "Opaque",
 | 
			
		||||
        "1": "Opaque"
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}"#
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										426
									
								
								crates/fayalite-visit-gen/src/lib.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										426
									
								
								crates/fayalite-visit-gen/src/lib.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,426 @@
 | 
			
		|||
// SPDX-License-Identifier: LGPL-3.0-or-later
 | 
			
		||||
// See Notices.txt for copyright information
 | 
			
		||||
use proc_macro2::{Span, TokenStream};
 | 
			
		||||
use quote::{format_ident, quote, ToTokens};
 | 
			
		||||
use std::{collections::BTreeMap, fs};
 | 
			
		||||
use syn::{fold::Fold, parse_quote};
 | 
			
		||||
 | 
			
		||||
pub mod ast;
 | 
			
		||||
 | 
			
		||||
fn map_camel_case_to_snake_case(s: &str) -> String {
 | 
			
		||||
    #[derive(Clone, Copy, PartialEq, Eq)]
 | 
			
		||||
    enum State {
 | 
			
		||||
        Start,
 | 
			
		||||
        Lowercase,
 | 
			
		||||
        PushedUpper(char),
 | 
			
		||||
    }
 | 
			
		||||
    let mut state = State::Start;
 | 
			
		||||
    let mut retval = String::new();
 | 
			
		||||
    for ch in s.chars() {
 | 
			
		||||
        state = match ch {
 | 
			
		||||
            'A'..='Z' => {
 | 
			
		||||
                match state {
 | 
			
		||||
                    State::Start => {}
 | 
			
		||||
                    State::Lowercase => retval.push('_'),
 | 
			
		||||
                    State::PushedUpper(upper) => retval.push(upper.to_ascii_lowercase()),
 | 
			
		||||
                }
 | 
			
		||||
                State::PushedUpper(ch)
 | 
			
		||||
            }
 | 
			
		||||
            _ => {
 | 
			
		||||
                match state {
 | 
			
		||||
                    State::PushedUpper(upper) => {
 | 
			
		||||
                        retval.push(upper.to_ascii_lowercase());
 | 
			
		||||
                    }
 | 
			
		||||
                    State::Start | State::Lowercase => {}
 | 
			
		||||
                }
 | 
			
		||||
                retval.push(ch);
 | 
			
		||||
                State::Lowercase
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
    match state {
 | 
			
		||||
        State::Lowercase | State::Start => {}
 | 
			
		||||
        State::PushedUpper(upper) => retval.push(upper.to_ascii_lowercase()),
 | 
			
		||||
    }
 | 
			
		||||
    retval
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone)]
 | 
			
		||||
struct DefinitionState {
 | 
			
		||||
    fn_name_suffix: syn::Ident,
 | 
			
		||||
    generics: syn::Generics,
 | 
			
		||||
    fold_generics: syn::Generics,
 | 
			
		||||
    folder_generics: syn::Generics,
 | 
			
		||||
    visit_generics: syn::Generics,
 | 
			
		||||
    visitor_generics: syn::Generics,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl DefinitionState {
 | 
			
		||||
    fn folder_fn_name(&self) -> syn::Ident {
 | 
			
		||||
        format_ident!("fold_{}", self.fn_name_suffix)
 | 
			
		||||
    }
 | 
			
		||||
    fn visitor_fn_name(&self) -> syn::Ident {
 | 
			
		||||
        format_ident!("visit_{}", self.fn_name_suffix)
 | 
			
		||||
    }
 | 
			
		||||
    fn folder_fn(&self, path: &ast::Path) -> TokenStream {
 | 
			
		||||
        let folder_fn_name = self.folder_fn_name();
 | 
			
		||||
        let (impl_generics, type_generics, where_clause) = self.folder_generics.split_for_impl();
 | 
			
		||||
        quote! {
 | 
			
		||||
            fn #folder_fn_name #impl_generics(&mut self, v: #path #type_generics) -> Result<#path #type_generics, Self::Error> #where_clause {
 | 
			
		||||
                Fold::default_fold(v, self)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    fn visitor_fn(&self, path: &ast::Path) -> TokenStream {
 | 
			
		||||
        let visitor_fn_name = self.visitor_fn_name();
 | 
			
		||||
        let (impl_generics, type_generics, where_clause) = self.visitor_generics.split_for_impl();
 | 
			
		||||
        quote! {
 | 
			
		||||
            fn #visitor_fn_name #impl_generics(&mut self, v: &#path #type_generics) -> Result<(), Self::Error> #where_clause {
 | 
			
		||||
                Visit::default_visit(v, self)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    fn fold_impl(&self, path: &ast::Path, body: impl ToTokens) -> TokenStream {
 | 
			
		||||
        let folder_fn_name = self.folder_fn_name();
 | 
			
		||||
        let (_, self_type_generics, _) = self.generics.split_for_impl();
 | 
			
		||||
        let (trait_impl_generics, _, trait_where_clause) = self.fold_generics.split_for_impl();
 | 
			
		||||
        quote! {
 | 
			
		||||
            #[automatically_derived]
 | 
			
		||||
            #[allow(clippy::init_numbered_fields)]
 | 
			
		||||
            impl #trait_impl_generics Fold<State> for #path #self_type_generics #trait_where_clause {
 | 
			
		||||
                fn fold(self, state: &mut State) -> Result<Self, State::Error> {
 | 
			
		||||
                    state.#folder_fn_name(self)
 | 
			
		||||
                }
 | 
			
		||||
                fn default_fold(self, state: &mut State) -> Result<Self, State::Error> {
 | 
			
		||||
                    #body
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    fn visit_impl(&self, path: &ast::Path, body: impl ToTokens) -> TokenStream {
 | 
			
		||||
        let visitor_fn_name = self.visitor_fn_name();
 | 
			
		||||
        let (_, self_type_generics, _) = self.generics.split_for_impl();
 | 
			
		||||
        let (trait_impl_generics, _, trait_where_clause) = self.visit_generics.split_for_impl();
 | 
			
		||||
        quote! {
 | 
			
		||||
            #[automatically_derived]
 | 
			
		||||
            impl #trait_impl_generics Visit<State> for #path #self_type_generics #trait_where_clause {
 | 
			
		||||
                fn visit(&self, state: &mut State) -> Result<(), State::Error> {
 | 
			
		||||
                    state.#visitor_fn_name(self)
 | 
			
		||||
                }
 | 
			
		||||
                fn default_visit(&self, state: &mut State) -> Result<(), State::Error> {
 | 
			
		||||
                    #body
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct GenerateState<'a> {
 | 
			
		||||
    def_states: BTreeMap<&'a ast::Path, DefinitionState>,
 | 
			
		||||
    definitions: &'a ast::Definitions,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct MapStateToSelf;
 | 
			
		||||
 | 
			
		||||
impl syn::fold::Fold for MapStateToSelf {
 | 
			
		||||
    fn fold_ident(&mut self, i: syn::Ident) -> syn::Ident {
 | 
			
		||||
        if i == "State" {
 | 
			
		||||
            syn::Ident::new("Self", i.span())
 | 
			
		||||
        } else {
 | 
			
		||||
            i
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> GenerateState<'a> {
 | 
			
		||||
    fn make_definition_state(&mut self, path: &'a ast::Path) -> syn::Result<()> {
 | 
			
		||||
        let ast::Definition {
 | 
			
		||||
            fn_name_suffix,
 | 
			
		||||
            generics,
 | 
			
		||||
            fold_where,
 | 
			
		||||
            visit_where,
 | 
			
		||||
            data: _,
 | 
			
		||||
        } = self.definitions.types.get(path).ok_or_else(|| {
 | 
			
		||||
            syn::Error::new(
 | 
			
		||||
                Span::call_site(),
 | 
			
		||||
                format!("can't find named type: {path:?}"),
 | 
			
		||||
            )
 | 
			
		||||
        })?;
 | 
			
		||||
        let fn_name_suffix = fn_name_suffix
 | 
			
		||||
            .as_ref()
 | 
			
		||||
            .map(syn::Ident::from)
 | 
			
		||||
            .unwrap_or_else(|| format_ident!("{}", map_camel_case_to_snake_case(&path.last().0)));
 | 
			
		||||
        let generics = generics.clone().map(|v| v.0).unwrap_or_default();
 | 
			
		||||
        let mut fold_generics = generics.clone();
 | 
			
		||||
        let mut folder_generics = generics.clone();
 | 
			
		||||
        fold_generics
 | 
			
		||||
            .params
 | 
			
		||||
            .insert(0, parse_quote! {State: ?Sized + Folder});
 | 
			
		||||
        if let Some(fold_where) = fold_where {
 | 
			
		||||
            fold_generics
 | 
			
		||||
                .make_where_clause()
 | 
			
		||||
                .predicates
 | 
			
		||||
                .extend(fold_where.0.iter().cloned());
 | 
			
		||||
            folder_generics.make_where_clause().predicates.extend(
 | 
			
		||||
                fold_where
 | 
			
		||||
                    .0
 | 
			
		||||
                    .iter()
 | 
			
		||||
                    .cloned()
 | 
			
		||||
                    .map(|v| MapStateToSelf.fold_where_predicate(v)),
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
        let mut visit_generics = generics.clone();
 | 
			
		||||
        let mut visitor_generics = generics.clone();
 | 
			
		||||
        visit_generics
 | 
			
		||||
            .params
 | 
			
		||||
            .insert(0, parse_quote! {State: ?Sized + Visitor});
 | 
			
		||||
        if let Some(visit_where) = visit_where {
 | 
			
		||||
            visit_generics
 | 
			
		||||
                .make_where_clause()
 | 
			
		||||
                .predicates
 | 
			
		||||
                .extend(visit_where.0.iter().cloned());
 | 
			
		||||
            visitor_generics.make_where_clause().predicates.extend(
 | 
			
		||||
                visit_where
 | 
			
		||||
                    .0
 | 
			
		||||
                    .iter()
 | 
			
		||||
                    .cloned()
 | 
			
		||||
                    .map(|v| MapStateToSelf.fold_where_predicate(v)),
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
        self.def_states.insert(
 | 
			
		||||
            path,
 | 
			
		||||
            DefinitionState {
 | 
			
		||||
                fn_name_suffix,
 | 
			
		||||
                generics,
 | 
			
		||||
                fold_generics,
 | 
			
		||||
                folder_generics,
 | 
			
		||||
                visit_generics,
 | 
			
		||||
                visitor_generics,
 | 
			
		||||
            },
 | 
			
		||||
        );
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
    fn new(ast: &'a ast::Definitions) -> syn::Result<Self> {
 | 
			
		||||
        let mut retval = GenerateState {
 | 
			
		||||
            def_states: BTreeMap::new(),
 | 
			
		||||
            definitions: ast,
 | 
			
		||||
        };
 | 
			
		||||
        let ast::Definitions { types } = ast;
 | 
			
		||||
        for path in types.keys() {
 | 
			
		||||
            retval.make_definition_state(path)?;
 | 
			
		||||
        }
 | 
			
		||||
        Ok(retval)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn generate(ast: &ast::Definitions) -> syn::Result<String> {
 | 
			
		||||
    let state = GenerateState::new(ast)?;
 | 
			
		||||
    let mut visitor_fns = vec![];
 | 
			
		||||
    let mut visit_impls = vec![];
 | 
			
		||||
    let mut folder_fns = vec![];
 | 
			
		||||
    let mut fold_impls = vec![];
 | 
			
		||||
    for (&def_path, def_state) in state.def_states.iter() {
 | 
			
		||||
        folder_fns.push(def_state.folder_fn(def_path));
 | 
			
		||||
        visitor_fns.push(def_state.visitor_fn(def_path));
 | 
			
		||||
        let fold_body;
 | 
			
		||||
        let visit_body;
 | 
			
		||||
        let ast::Definition {
 | 
			
		||||
            fn_name_suffix: _,
 | 
			
		||||
            generics: _,
 | 
			
		||||
            fold_where: _,
 | 
			
		||||
            visit_where: _,
 | 
			
		||||
            data,
 | 
			
		||||
        } = ast.types.get(def_path).unwrap();
 | 
			
		||||
        match data {
 | 
			
		||||
            ast::Data::ManualImpl => {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            ast::Data::Opaque => {
 | 
			
		||||
                fold_body = quote! {
 | 
			
		||||
                    let _ = state;
 | 
			
		||||
                    Ok(self)
 | 
			
		||||
                };
 | 
			
		||||
                visit_body = quote! {
 | 
			
		||||
                    let _ = state;
 | 
			
		||||
                    Ok(())
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
            ast::Data::Struct(ast::Fields {
 | 
			
		||||
                constructor,
 | 
			
		||||
                fields,
 | 
			
		||||
            }) => {
 | 
			
		||||
                let mut visit_members = vec![];
 | 
			
		||||
                let mut fold_members = vec![];
 | 
			
		||||
                for (field_name, field) in fields {
 | 
			
		||||
                    let fold_member_name = if constructor.is_some() {
 | 
			
		||||
                        None
 | 
			
		||||
                    } else {
 | 
			
		||||
                        let member = field_name.to_member();
 | 
			
		||||
                        if member.is_none() {
 | 
			
		||||
                            return Err(syn::Error::new(Span::call_site(), format!("struct must have `$constructor` since it contains a non-plain field: {def_path:?} {field_name:?}")));
 | 
			
		||||
                        }
 | 
			
		||||
                        member
 | 
			
		||||
                    };
 | 
			
		||||
                    let fold_member_name = fold_member_name.as_slice();
 | 
			
		||||
                    let fold_member = match field {
 | 
			
		||||
                        ast::Field::Opaque => {
 | 
			
		||||
                            quote! {
 | 
			
		||||
                                #(#fold_member_name:)* self.#field_name
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        ast::Field::Visible => {
 | 
			
		||||
                            visit_members.push(quote! {
 | 
			
		||||
                                Visit::visit(&self.#field_name, state)?;
 | 
			
		||||
                            });
 | 
			
		||||
                            quote! {
 | 
			
		||||
                                #(#fold_member_name:)* Fold::fold(self.#field_name, state)?
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        ast::Field::RefVisible => {
 | 
			
		||||
                            visit_members.push(quote! {
 | 
			
		||||
                                Visit::visit(self.#field_name, state)?;
 | 
			
		||||
                            });
 | 
			
		||||
                            quote! {
 | 
			
		||||
                                #(#fold_member_name:)* Fold::fold(self.#field_name.clone(), state)?
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    };
 | 
			
		||||
                    fold_members.push(fold_member);
 | 
			
		||||
                }
 | 
			
		||||
                let match_members = constructor
 | 
			
		||||
                    .is_none()
 | 
			
		||||
                    .then(|| {
 | 
			
		||||
                        fields
 | 
			
		||||
                            .keys()
 | 
			
		||||
                            .map(|k| k.to_member())
 | 
			
		||||
                            .collect::<Option<Vec<_>>>()
 | 
			
		||||
                            .map(|members| {
 | 
			
		||||
                                if members.is_empty() {
 | 
			
		||||
                                    quote! {
 | 
			
		||||
                                        let _ = state;
 | 
			
		||||
                                        let Self {} = self;
 | 
			
		||||
                                    }
 | 
			
		||||
                                } else {
 | 
			
		||||
                                    quote! {
 | 
			
		||||
                                        let Self {
 | 
			
		||||
                                            #(#members: _,)*
 | 
			
		||||
                                        } = self;
 | 
			
		||||
                                    }
 | 
			
		||||
                                }
 | 
			
		||||
                            })
 | 
			
		||||
                    })
 | 
			
		||||
                    .flatten();
 | 
			
		||||
                visit_body = quote! {
 | 
			
		||||
                    #match_members
 | 
			
		||||
                    #(#visit_members)*
 | 
			
		||||
                    Ok(())
 | 
			
		||||
                };
 | 
			
		||||
                let fold_body_tail = if let Some(constructor) = constructor {
 | 
			
		||||
                    quote! {
 | 
			
		||||
                        Ok(#constructor(#(#fold_members),*))
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    quote! {
 | 
			
		||||
                        Ok(Self {
 | 
			
		||||
                            #(#fold_members,)*
 | 
			
		||||
                        })
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
                fold_body = quote! {
 | 
			
		||||
                    #match_members
 | 
			
		||||
                    #fold_body_tail
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
            ast::Data::Enum(ast::Variants { variants }) => {
 | 
			
		||||
                let mut fold_arms = vec![];
 | 
			
		||||
                let mut visit_arms = vec![];
 | 
			
		||||
                let mut state_unused = true;
 | 
			
		||||
                for (variant_name, variant_field) in variants {
 | 
			
		||||
                    let fold_arm;
 | 
			
		||||
                    let visit_arm;
 | 
			
		||||
                    match variant_field {
 | 
			
		||||
                        Some(ast::Field::Visible) => {
 | 
			
		||||
                            state_unused = false;
 | 
			
		||||
                            fold_arm = quote! {
 | 
			
		||||
                                Self::#variant_name(v) => Fold::fold(v, state).map(Self::#variant_name),
 | 
			
		||||
                            };
 | 
			
		||||
                            visit_arm = quote! {
 | 
			
		||||
                                Self::#variant_name(v) => Visit::visit(v, state),
 | 
			
		||||
                            };
 | 
			
		||||
                        }
 | 
			
		||||
                        Some(ast::Field::RefVisible) => {
 | 
			
		||||
                            return Err(syn::Error::new(
 | 
			
		||||
                                Span::call_site(),
 | 
			
		||||
                                "enum variant field must not be RefVisible",
 | 
			
		||||
                            ));
 | 
			
		||||
                        }
 | 
			
		||||
                        Some(ast::Field::Opaque) => {
 | 
			
		||||
                            fold_arm = quote! {
 | 
			
		||||
                                Self::#variant_name(_) => Ok(self),
 | 
			
		||||
                            };
 | 
			
		||||
                            visit_arm = quote! {
 | 
			
		||||
                                Self::#variant_name(_) => Ok(()),
 | 
			
		||||
                            };
 | 
			
		||||
                        }
 | 
			
		||||
                        None => {
 | 
			
		||||
                            fold_arm = quote! {
 | 
			
		||||
                                Self::#variant_name => Ok(self),
 | 
			
		||||
                            };
 | 
			
		||||
                            visit_arm = quote! {
 | 
			
		||||
                                Self::#variant_name => Ok(()),
 | 
			
		||||
                            };
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    fold_arms.push(fold_arm);
 | 
			
		||||
                    visit_arms.push(visit_arm);
 | 
			
		||||
                }
 | 
			
		||||
                let ignore_state = state_unused.then(|| {
 | 
			
		||||
                    quote! {
 | 
			
		||||
                        let _ = state;
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
                visit_body = quote! {
 | 
			
		||||
                    #ignore_state
 | 
			
		||||
                    match self {
 | 
			
		||||
                        #(#visit_arms)*
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
                fold_body = quote! {
 | 
			
		||||
                    #ignore_state
 | 
			
		||||
                    match self {
 | 
			
		||||
                        #(#fold_arms)*
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        fold_impls.push(def_state.fold_impl(def_path, fold_body));
 | 
			
		||||
        visit_impls.push(def_state.visit_impl(def_path, visit_body));
 | 
			
		||||
    }
 | 
			
		||||
    Ok(prettyplease::unparse(&parse_quote! {
 | 
			
		||||
        pub trait Visitor {
 | 
			
		||||
            type Error;
 | 
			
		||||
 | 
			
		||||
            #(#visitor_fns)*
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #(#visit_impls)*
 | 
			
		||||
 | 
			
		||||
        pub trait Folder {
 | 
			
		||||
            type Error;
 | 
			
		||||
 | 
			
		||||
            #(#folder_fns)*
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #(#fold_impls)*
 | 
			
		||||
    }))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn error_at_call_site<T: std::fmt::Display>(e: T) -> syn::Error {
 | 
			
		||||
    syn::Error::new(Span::call_site(), e)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn parse_and_generate(path: impl AsRef<std::path::Path>) -> syn::Result<String> {
 | 
			
		||||
    let input = fs::read_to_string(path).map_err(error_at_call_site)?;
 | 
			
		||||
    let ast: ast::Definitions = serde_json::from_str(&input).map_err(error_at_call_site)?;
 | 
			
		||||
    generate(&ast)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										24
									
								
								crates/fayalite/Cargo.toml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								crates/fayalite/Cargo.toml
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,24 @@
 | 
			
		|||
# SPDX-License-Identifier: LGPL-3.0-or-later
 | 
			
		||||
# See Notices.txt for copyright information
 | 
			
		||||
[package]
 | 
			
		||||
name = "fayalite"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
edition = "2021"
 | 
			
		||||
workspace = "../.."
 | 
			
		||||
license = "LGPL-3.0-or-later"
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
bitvec = { version = "1.0.1", features = ["serde"] }
 | 
			
		||||
hashbrown = "0.14.3"
 | 
			
		||||
num-bigint = "0.4.4"
 | 
			
		||||
num-traits = "0.2.16"
 | 
			
		||||
paste = "1.0.14"
 | 
			
		||||
fayalite-proc-macros = { version = "=0.1.0", path = "../fayalite-proc-macros" }
 | 
			
		||||
serde = { version = "1.0.202", features = ["derive"] }
 | 
			
		||||
serde_json = "1.0.117"
 | 
			
		||||
 | 
			
		||||
[dev-dependencies]
 | 
			
		||||
trybuild = "1.0"
 | 
			
		||||
 | 
			
		||||
[build-dependencies]
 | 
			
		||||
fayalite-visit-gen = { version = "=0.1.0", path = "../fayalite-visit-gen" }
 | 
			
		||||
							
								
								
									
										15
									
								
								crates/fayalite/build.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								crates/fayalite/build.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,15 @@
 | 
			
		|||
// SPDX-License-Identifier: LGPL-3.0-or-later
 | 
			
		||||
// See Notices.txt for copyright information
 | 
			
		||||
use fayalite_visit_gen::parse_and_generate;
 | 
			
		||||
use std::{env, fs, path::Path};
 | 
			
		||||
 | 
			
		||||
fn main() {
 | 
			
		||||
    let path = "visit_types.json";
 | 
			
		||||
    println!("cargo::rerun-if-changed={path}");
 | 
			
		||||
    println!("cargo::rerun-if-changed=build.rs");
 | 
			
		||||
    let generated = parse_and_generate(path).map_err(|e| panic!("{e}")).unwrap();
 | 
			
		||||
    let out_dir = env::var_os("OUT_DIR").unwrap();
 | 
			
		||||
    let out_path = Path::new(&out_dir).join("visit.rs");
 | 
			
		||||
    fs::write(&out_path, generated).unwrap();
 | 
			
		||||
    // println!("cargo::warning=generated {}", out_path.display());
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										196
									
								
								crates/fayalite/src/annotations.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										196
									
								
								crates/fayalite/src/annotations.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,196 @@
 | 
			
		|||
// SPDX-License-Identifier: LGPL-3.0-or-later
 | 
			
		||||
// See Notices.txt for copyright information
 | 
			
		||||
use crate::{
 | 
			
		||||
    expr::Target,
 | 
			
		||||
    intern::{Intern, Interned},
 | 
			
		||||
};
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
use std::{
 | 
			
		||||
    fmt,
 | 
			
		||||
    hash::{Hash, Hasher},
 | 
			
		||||
    ops::Deref,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#[derive(Clone)]
 | 
			
		||||
struct CustomFirrtlAnnotationFieldsImpl {
 | 
			
		||||
    value: serde_json::Map<String, serde_json::Value>,
 | 
			
		||||
    serialized: Interned<str>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Hash for CustomFirrtlAnnotationFieldsImpl {
 | 
			
		||||
    fn hash<H: Hasher>(&self, state: &mut H) {
 | 
			
		||||
        self.serialized.hash(state);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Eq for CustomFirrtlAnnotationFieldsImpl {}
 | 
			
		||||
 | 
			
		||||
impl PartialEq for CustomFirrtlAnnotationFieldsImpl {
 | 
			
		||||
    fn eq(&self, other: &Self) -> bool {
 | 
			
		||||
        self.serialized == other.serialized
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
 | 
			
		||||
pub struct CustomFirrtlAnnotationFields(Interned<CustomFirrtlAnnotationFieldsImpl>);
 | 
			
		||||
 | 
			
		||||
impl fmt::Debug for CustomFirrtlAnnotationFields {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        self.0.value.fmt(f)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'de> Deserialize<'de> for CustomFirrtlAnnotationFields {
 | 
			
		||||
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
 | 
			
		||||
    where
 | 
			
		||||
        D: serde::Deserializer<'de>,
 | 
			
		||||
    {
 | 
			
		||||
        serde_json::Map::<String, serde_json::Value>::deserialize(deserializer).map(Self::from)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Serialize for CustomFirrtlAnnotationFields {
 | 
			
		||||
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
 | 
			
		||||
    where
 | 
			
		||||
        S: serde::Serializer,
 | 
			
		||||
    {
 | 
			
		||||
        self.0.value.serialize(serializer)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Deref for CustomFirrtlAnnotationFields {
 | 
			
		||||
    type Target = serde_json::Map<String, serde_json::Value>;
 | 
			
		||||
 | 
			
		||||
    fn deref(&self) -> &Self::Target {
 | 
			
		||||
        &self.0.value
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<serde_json::Map<String, serde_json::Value>> for CustomFirrtlAnnotationFields {
 | 
			
		||||
    fn from(value: serde_json::Map<String, serde_json::Value>) -> Self {
 | 
			
		||||
        let serialized =
 | 
			
		||||
            serde_json::to_string(&value).expect("serialization of JSON should succeed");
 | 
			
		||||
        Self(Intern::intern_sized(CustomFirrtlAnnotationFieldsImpl {
 | 
			
		||||
            value,
 | 
			
		||||
            serialized: Intern::intern_owned(serialized),
 | 
			
		||||
        }))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
pub struct NotAJsonObject(pub serde_json::Value);
 | 
			
		||||
 | 
			
		||||
impl fmt::Display for NotAJsonObject {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        f.write_str("not a JSON object")
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl std::error::Error for NotAJsonObject {}
 | 
			
		||||
 | 
			
		||||
impl TryFrom<serde_json::Value> for CustomFirrtlAnnotationFields {
 | 
			
		||||
    type Error = NotAJsonObject;
 | 
			
		||||
 | 
			
		||||
    fn try_from(value: serde_json::Value) -> Result<Self, Self::Error> {
 | 
			
		||||
        match value {
 | 
			
		||||
            serde_json::Value::Object(value) => Ok(value.into()),
 | 
			
		||||
            _ => Err(NotAJsonObject(value)),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<CustomFirrtlAnnotationFields> for serde_json::Map<String, serde_json::Value> {
 | 
			
		||||
    fn from(value: CustomFirrtlAnnotationFields) -> Self {
 | 
			
		||||
        Self::clone(&value)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<CustomFirrtlAnnotationFields> for serde_json::Value {
 | 
			
		||||
    fn from(value: CustomFirrtlAnnotationFields) -> Self {
 | 
			
		||||
        serde_json::Value::Object(value.into())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)]
 | 
			
		||||
pub struct CustomFirrtlAnnotation {
 | 
			
		||||
    pub class: Interned<str>,
 | 
			
		||||
    #[serde(flatten)]
 | 
			
		||||
    pub additional_fields: CustomFirrtlAnnotationFields,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
 | 
			
		||||
#[non_exhaustive]
 | 
			
		||||
pub enum Annotation {
 | 
			
		||||
    DontTouch,
 | 
			
		||||
    CustomFirrtl(CustomFirrtlAnnotation),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
 | 
			
		||||
pub struct TargetedAnnotation {
 | 
			
		||||
    target: Interned<Target>,
 | 
			
		||||
    annotation: Annotation,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl TargetedAnnotation {
 | 
			
		||||
    #[track_caller]
 | 
			
		||||
    pub fn new(target: Interned<Target>, annotation: Annotation) -> Self {
 | 
			
		||||
        Self::assert_valid_target(target);
 | 
			
		||||
        Self { target, annotation }
 | 
			
		||||
    }
 | 
			
		||||
    #[track_caller]
 | 
			
		||||
    pub fn assert_valid_target(target: Interned<Target>) {
 | 
			
		||||
        assert!(target.is_static(), "can't annotate non-static targets");
 | 
			
		||||
    }
 | 
			
		||||
    pub fn target(&self) -> Interned<Target> {
 | 
			
		||||
        self.target
 | 
			
		||||
    }
 | 
			
		||||
    pub fn annotation(&self) -> &Annotation {
 | 
			
		||||
        &self.annotation
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub trait IntoAnnotations {
 | 
			
		||||
    type IntoAnnotations: IntoIterator<Item = Annotation>;
 | 
			
		||||
 | 
			
		||||
    fn into_annotations(self) -> Self::IntoAnnotations;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl IntoAnnotations for Annotation {
 | 
			
		||||
    type IntoAnnotations = [Annotation; 1];
 | 
			
		||||
 | 
			
		||||
    fn into_annotations(self) -> Self::IntoAnnotations {
 | 
			
		||||
        [self]
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl IntoAnnotations for Box<Annotation> {
 | 
			
		||||
    type IntoAnnotations = [Annotation; 1];
 | 
			
		||||
 | 
			
		||||
    fn into_annotations(self) -> Self::IntoAnnotations {
 | 
			
		||||
        [*self]
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl IntoAnnotations for &'_ Annotation {
 | 
			
		||||
    type IntoAnnotations = [Annotation; 1];
 | 
			
		||||
 | 
			
		||||
    fn into_annotations(self) -> Self::IntoAnnotations {
 | 
			
		||||
        [self.clone()]
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl IntoAnnotations for &'_ mut Annotation {
 | 
			
		||||
    type IntoAnnotations = [Annotation; 1];
 | 
			
		||||
 | 
			
		||||
    fn into_annotations(self) -> Self::IntoAnnotations {
 | 
			
		||||
        [self.clone()]
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: IntoIterator<Item = Annotation>> IntoAnnotations for T {
 | 
			
		||||
    type IntoAnnotations = Self;
 | 
			
		||||
 | 
			
		||||
    fn into_annotations(self) -> Self::IntoAnnotations {
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										729
									
								
								crates/fayalite/src/array.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										729
									
								
								crates/fayalite/src/array.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,729 @@
 | 
			
		|||
// SPDX-License-Identifier: LGPL-3.0-or-later
 | 
			
		||||
// See Notices.txt for copyright information
 | 
			
		||||
use crate::{
 | 
			
		||||
    bundle::{BundleType, BundleValue},
 | 
			
		||||
    expr::{
 | 
			
		||||
        ops::{ArrayIndex, ArrayLiteral, ExprIndex},
 | 
			
		||||
        Expr, ToExpr,
 | 
			
		||||
    },
 | 
			
		||||
    intern::{Intern, Interned, InternedCompare, Memoize},
 | 
			
		||||
    module::{
 | 
			
		||||
        transform::visit::{Fold, Folder, Visit, Visitor},
 | 
			
		||||
        ModuleBuilder, NormalModule,
 | 
			
		||||
    },
 | 
			
		||||
    source_location::SourceLocation,
 | 
			
		||||
    ty::{
 | 
			
		||||
        CanonicalType, CanonicalTypeKind, CanonicalValue, Connect, DynCanonicalType,
 | 
			
		||||
        DynCanonicalValue, DynType, DynValueTrait, FixedType, MatchVariantWithoutScope, Type,
 | 
			
		||||
        TypeEnum, Value, ValueEnum,
 | 
			
		||||
    },
 | 
			
		||||
    util::{ConstBool, GenericConstBool, MakeMutSlice},
 | 
			
		||||
};
 | 
			
		||||
use bitvec::{slice::BitSlice, vec::BitVec};
 | 
			
		||||
use std::{
 | 
			
		||||
    any::Any,
 | 
			
		||||
    borrow::{Borrow, BorrowMut},
 | 
			
		||||
    fmt,
 | 
			
		||||
    hash::Hash,
 | 
			
		||||
    marker::PhantomData,
 | 
			
		||||
    ops::IndexMut,
 | 
			
		||||
    sync::Arc,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
mod sealed {
 | 
			
		||||
    pub trait Sealed {}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub trait ValueArrayOrSlice:
 | 
			
		||||
    sealed::Sealed
 | 
			
		||||
    + BorrowMut<[<Self as ValueArrayOrSlice>::Element]>
 | 
			
		||||
    + AsRef<[<Self as ValueArrayOrSlice>::Element]>
 | 
			
		||||
    + AsMut<[<Self as ValueArrayOrSlice>::Element]>
 | 
			
		||||
    + Hash
 | 
			
		||||
    + fmt::Debug
 | 
			
		||||
    + Eq
 | 
			
		||||
    + Send
 | 
			
		||||
    + Sync
 | 
			
		||||
    + 'static
 | 
			
		||||
    + IndexMut<usize, Output = <Self as ValueArrayOrSlice>::Element>
 | 
			
		||||
    + ToOwned
 | 
			
		||||
    + InternedCompare
 | 
			
		||||
{
 | 
			
		||||
    type Element: Value<Type = <Self as ValueArrayOrSlice>::ElementType>;
 | 
			
		||||
    type ElementType: Type<Value = <Self as ValueArrayOrSlice>::Element>;
 | 
			
		||||
    type LenType: 'static + Copy + Ord + fmt::Debug + Hash + Send + Sync;
 | 
			
		||||
    type Match: 'static
 | 
			
		||||
        + Clone
 | 
			
		||||
        + Eq
 | 
			
		||||
        + fmt::Debug
 | 
			
		||||
        + Hash
 | 
			
		||||
        + Send
 | 
			
		||||
        + Sync
 | 
			
		||||
        + BorrowMut<[Expr<Self::Element>]>;
 | 
			
		||||
    type MaskVA: ValueArrayOrSlice<
 | 
			
		||||
            Element = <Self::ElementType as Type>::MaskValue,
 | 
			
		||||
            ElementType = <Self::ElementType as Type>::MaskType,
 | 
			
		||||
            LenType = Self::LenType,
 | 
			
		||||
            MaskVA = Self::MaskVA,
 | 
			
		||||
        > + ?Sized;
 | 
			
		||||
    type IsFixedLen: GenericConstBool;
 | 
			
		||||
    const FIXED_LEN_TYPE: Option<Self::LenType>;
 | 
			
		||||
    fn make_match(array: Expr<Array<Self>>) -> Self::Match;
 | 
			
		||||
    fn len_from_len_type(v: Self::LenType) -> usize;
 | 
			
		||||
    #[allow(clippy::result_unit_err)]
 | 
			
		||||
    fn try_len_type_from_len(v: usize) -> Result<Self::LenType, ()>;
 | 
			
		||||
    fn len_type(&self) -> Self::LenType;
 | 
			
		||||
    fn len(&self) -> usize;
 | 
			
		||||
    fn is_empty(&self) -> bool;
 | 
			
		||||
    fn iter(&self) -> std::slice::Iter<Self::Element> {
 | 
			
		||||
        Borrow::<[_]>::borrow(self).iter()
 | 
			
		||||
    }
 | 
			
		||||
    fn clone_to_arc(&self) -> Arc<Self>;
 | 
			
		||||
    fn arc_make_mut(v: &mut Arc<Self>) -> &mut Self;
 | 
			
		||||
    fn arc_to_arc_slice(self: Arc<Self>) -> Arc<[Self::Element]>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T> sealed::Sealed for [T] {}
 | 
			
		||||
 | 
			
		||||
impl<V: Value> ValueArrayOrSlice for [V]
 | 
			
		||||
where
 | 
			
		||||
    V::Type: Type<Value = V>,
 | 
			
		||||
{
 | 
			
		||||
    type Element = V;
 | 
			
		||||
    type ElementType = V::Type;
 | 
			
		||||
    type LenType = usize;
 | 
			
		||||
    type Match = Box<[Expr<V>]>;
 | 
			
		||||
    type MaskVA = [<Self::ElementType as Type>::MaskValue];
 | 
			
		||||
    type IsFixedLen = ConstBool<false>;
 | 
			
		||||
    const FIXED_LEN_TYPE: Option<Self::LenType> = None;
 | 
			
		||||
 | 
			
		||||
    fn make_match(array: Expr<Array<Self>>) -> Self::Match {
 | 
			
		||||
        (0..array.canonical_type().len())
 | 
			
		||||
            .map(|index| ArrayIndex::<V::Type>::new_unchecked(array.canonical(), index).to_expr())
 | 
			
		||||
            .collect()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn len_from_len_type(v: Self::LenType) -> usize {
 | 
			
		||||
        v
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn try_len_type_from_len(v: usize) -> Result<Self::LenType, ()> {
 | 
			
		||||
        Ok(v)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn len_type(&self) -> Self::LenType {
 | 
			
		||||
        self.len()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn len(&self) -> usize {
 | 
			
		||||
        <[_]>::len(self)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn is_empty(&self) -> bool {
 | 
			
		||||
        <[_]>::is_empty(self)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn clone_to_arc(&self) -> Arc<Self> {
 | 
			
		||||
        Arc::from(self)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn arc_make_mut(v: &mut Arc<Self>) -> &mut Self {
 | 
			
		||||
        MakeMutSlice::make_mut_slice(v)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn arc_to_arc_slice(self: Arc<Self>) -> Arc<[Self::Element]> {
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T, const N: usize> sealed::Sealed for [T; N] {}
 | 
			
		||||
 | 
			
		||||
impl<V: Value, const N: usize> ValueArrayOrSlice for [V; N]
 | 
			
		||||
where
 | 
			
		||||
    V::Type: Type<Value = V>,
 | 
			
		||||
{
 | 
			
		||||
    type Element = V;
 | 
			
		||||
    type ElementType = V::Type;
 | 
			
		||||
    type LenType = ();
 | 
			
		||||
    type Match = [Expr<V>; N];
 | 
			
		||||
    type MaskVA = [<Self::ElementType as Type>::MaskValue; N];
 | 
			
		||||
    type IsFixedLen = ConstBool<true>;
 | 
			
		||||
    const FIXED_LEN_TYPE: Option<Self::LenType> = Some(());
 | 
			
		||||
 | 
			
		||||
    fn make_match(array: Expr<Array<Self>>) -> Self::Match {
 | 
			
		||||
        std::array::from_fn(|index| {
 | 
			
		||||
            ArrayIndex::<V::Type>::new_unchecked(array.canonical(), index).to_expr()
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn len_from_len_type(_v: Self::LenType) -> usize {
 | 
			
		||||
        N
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn try_len_type_from_len(v: usize) -> Result<Self::LenType, ()> {
 | 
			
		||||
        if v == N {
 | 
			
		||||
            Ok(())
 | 
			
		||||
        } else {
 | 
			
		||||
            Err(())
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn len_type(&self) -> Self::LenType {}
 | 
			
		||||
 | 
			
		||||
    fn len(&self) -> usize {
 | 
			
		||||
        N
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn is_empty(&self) -> bool {
 | 
			
		||||
        N == 0
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn clone_to_arc(&self) -> Arc<Self> {
 | 
			
		||||
        Arc::new(self.clone())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn arc_make_mut(v: &mut Arc<Self>) -> &mut Self {
 | 
			
		||||
        Arc::make_mut(v)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn arc_to_arc_slice(self: Arc<Self>) -> Arc<[Self::Element]> {
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, PartialEq, Eq, Hash)]
 | 
			
		||||
pub struct ArrayType<VA: ValueArrayOrSlice + ?Sized> {
 | 
			
		||||
    element: VA::ElementType,
 | 
			
		||||
    len: VA::LenType,
 | 
			
		||||
    bit_width: usize,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub trait ArrayTypeTrait:
 | 
			
		||||
    Type<
 | 
			
		||||
        CanonicalType = ArrayType<[DynCanonicalValue]>,
 | 
			
		||||
        Value = Array<<Self as ArrayTypeTrait>::ValueArrayOrSlice>,
 | 
			
		||||
        CanonicalValue = Array<[DynCanonicalValue]>,
 | 
			
		||||
        MaskType = ArrayType<
 | 
			
		||||
            <<Self as ArrayTypeTrait>::ValueArrayOrSlice as ValueArrayOrSlice>::MaskVA,
 | 
			
		||||
        >,
 | 
			
		||||
    > + From<ArrayType<<Self as ArrayTypeTrait>::ValueArrayOrSlice>>
 | 
			
		||||
    + Into<ArrayType<<Self as ArrayTypeTrait>::ValueArrayOrSlice>>
 | 
			
		||||
    + BorrowMut<ArrayType<<Self as ArrayTypeTrait>::ValueArrayOrSlice>>
 | 
			
		||||
    + sealed::Sealed
 | 
			
		||||
    + Connect<Self>
 | 
			
		||||
{
 | 
			
		||||
    type ValueArrayOrSlice: ValueArrayOrSlice<Element = Self::Element, ElementType = Self::ElementType>
 | 
			
		||||
        + ?Sized;
 | 
			
		||||
    type Element: Value<Type = Self::ElementType>;
 | 
			
		||||
    type ElementType: Type<Value = Self::Element>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<VA: ValueArrayOrSlice + ?Sized> sealed::Sealed for ArrayType<VA> {}
 | 
			
		||||
 | 
			
		||||
impl<VA: ValueArrayOrSlice + ?Sized> ArrayTypeTrait for ArrayType<VA> {
 | 
			
		||||
    type ValueArrayOrSlice = VA;
 | 
			
		||||
    type Element = VA::Element;
 | 
			
		||||
    type ElementType = VA::ElementType;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<VA: ValueArrayOrSlice + ?Sized> Clone for ArrayType<VA> {
 | 
			
		||||
    fn clone(&self) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            element: self.element.clone(),
 | 
			
		||||
            len: self.len,
 | 
			
		||||
            bit_width: self.bit_width,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<VA: ValueArrayOrSlice + ?Sized> Copy for ArrayType<VA> where VA::ElementType: Copy {}
 | 
			
		||||
 | 
			
		||||
impl<VA: ValueArrayOrSlice + ?Sized> ArrayType<VA> {
 | 
			
		||||
    pub fn element(&self) -> &VA::ElementType {
 | 
			
		||||
        &self.element
 | 
			
		||||
    }
 | 
			
		||||
    pub fn len(&self) -> usize {
 | 
			
		||||
        VA::len_from_len_type(self.len)
 | 
			
		||||
    }
 | 
			
		||||
    pub fn is_empty(&self) -> bool {
 | 
			
		||||
        self.len() == 0
 | 
			
		||||
    }
 | 
			
		||||
    pub fn bit_width(&self) -> usize {
 | 
			
		||||
        self.bit_width
 | 
			
		||||
    }
 | 
			
		||||
    pub fn into_slice_type(self) -> ArrayType<[VA::Element]> {
 | 
			
		||||
        ArrayType {
 | 
			
		||||
            len: self.len(),
 | 
			
		||||
            element: self.element,
 | 
			
		||||
            bit_width: self.bit_width,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    #[track_caller]
 | 
			
		||||
    pub fn new_with_len(element: VA::ElementType, len: usize) -> Self {
 | 
			
		||||
        Self::new_with_len_type(
 | 
			
		||||
            element,
 | 
			
		||||
            VA::try_len_type_from_len(len).expect("length should match"),
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
    #[track_caller]
 | 
			
		||||
    pub fn new_with_len_type(element: VA::ElementType, len: VA::LenType) -> Self {
 | 
			
		||||
        let Some(bit_width) = VA::len_from_len_type(len).checked_mul(element.bit_width()) else {
 | 
			
		||||
            panic!("array is too big: bit-width overflowed");
 | 
			
		||||
        };
 | 
			
		||||
        ArrayType {
 | 
			
		||||
            element,
 | 
			
		||||
            len,
 | 
			
		||||
            bit_width,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<VA: ValueArrayOrSlice + ?Sized, State: ?Sized + Folder> Fold<State> for ArrayType<VA>
 | 
			
		||||
where
 | 
			
		||||
    VA::ElementType: Fold<State>,
 | 
			
		||||
{
 | 
			
		||||
    fn fold(self, state: &mut State) -> Result<Self, State::Error> {
 | 
			
		||||
        state.fold_array_type(self)
 | 
			
		||||
    }
 | 
			
		||||
    fn default_fold(self, state: &mut State) -> Result<Self, State::Error> {
 | 
			
		||||
        Ok(Self::new_with_len_type(self.element.fold(state)?, self.len))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<VA: ValueArrayOrSlice + ?Sized, State: ?Sized + Visitor> Visit<State> for ArrayType<VA>
 | 
			
		||||
where
 | 
			
		||||
    VA::ElementType: Visit<State>,
 | 
			
		||||
{
 | 
			
		||||
    fn visit(&self, state: &mut State) -> Result<(), State::Error> {
 | 
			
		||||
        state.visit_array_type(self)
 | 
			
		||||
    }
 | 
			
		||||
    fn default_visit(&self, state: &mut State) -> Result<(), State::Error> {
 | 
			
		||||
        self.element.visit(state)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<V: Value, const N: usize> ArrayType<[V; N]>
 | 
			
		||||
where
 | 
			
		||||
    V::Type: Type<Value = V>,
 | 
			
		||||
{
 | 
			
		||||
    pub fn new_array(element: V::Type) -> Self {
 | 
			
		||||
        ArrayType::new_with_len_type(element, ())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<V: Value, const N: usize> FixedType for ArrayType<[V; N]>
 | 
			
		||||
where
 | 
			
		||||
    V::Type: FixedType<Value = V>,
 | 
			
		||||
{
 | 
			
		||||
    fn fixed_type() -> Self {
 | 
			
		||||
        Self::new_array(FixedType::fixed_type())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<V: Value> ArrayType<[V]>
 | 
			
		||||
where
 | 
			
		||||
    V::Type: Type<Value = V>,
 | 
			
		||||
{
 | 
			
		||||
    pub fn new_slice(element: V::Type, len: usize) -> Self {
 | 
			
		||||
        ArrayType::new_with_len_type(element, len)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<VA: ValueArrayOrSlice + ?Sized> Type for ArrayType<VA> {
 | 
			
		||||
    type CanonicalType = ArrayType<[DynCanonicalValue]>;
 | 
			
		||||
    type Value = Array<VA>;
 | 
			
		||||
    type CanonicalValue = Array<[DynCanonicalValue]>;
 | 
			
		||||
    type MaskType = ArrayType<VA::MaskVA>;
 | 
			
		||||
    type MaskValue = Array<VA::MaskVA>;
 | 
			
		||||
    type MatchVariant = VA::Match;
 | 
			
		||||
    type MatchActiveScope = ();
 | 
			
		||||
    type MatchVariantAndInactiveScope = MatchVariantWithoutScope<VA::Match>;
 | 
			
		||||
    type MatchVariantsIter = std::iter::Once<Self::MatchVariantAndInactiveScope>;
 | 
			
		||||
 | 
			
		||||
    fn match_variants<IO: BundleValue>(
 | 
			
		||||
        this: Expr<Self::Value>,
 | 
			
		||||
        module_builder: &mut ModuleBuilder<IO, NormalModule>,
 | 
			
		||||
        source_location: SourceLocation,
 | 
			
		||||
    ) -> Self::MatchVariantsIter
 | 
			
		||||
    where
 | 
			
		||||
        IO::Type: BundleType<Value = IO>,
 | 
			
		||||
    {
 | 
			
		||||
        let _ = module_builder;
 | 
			
		||||
        let _ = source_location;
 | 
			
		||||
        std::iter::once(MatchVariantWithoutScope(VA::make_match(this)))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn mask_type(&self) -> Self::MaskType {
 | 
			
		||||
        #[derive(Clone, Hash, Eq, PartialEq)]
 | 
			
		||||
        struct ArrayMaskTypeMemoize<T: ArrayTypeTrait>(PhantomData<T>);
 | 
			
		||||
        impl<T: ArrayTypeTrait> Copy for ArrayMaskTypeMemoize<T> {}
 | 
			
		||||
        impl<T: ArrayTypeTrait> Memoize for ArrayMaskTypeMemoize<T> {
 | 
			
		||||
            type Input = ArrayType<T::ValueArrayOrSlice>;
 | 
			
		||||
            type InputOwned = ArrayType<T::ValueArrayOrSlice>;
 | 
			
		||||
            type Output = <ArrayType<T::ValueArrayOrSlice> as Type>::MaskType;
 | 
			
		||||
 | 
			
		||||
            fn inner(self, input: &Self::Input) -> Self::Output {
 | 
			
		||||
                ArrayType::new_with_len_type(input.element.mask_type(), input.len)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        ArrayMaskTypeMemoize::<Self>(PhantomData).get(self)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn canonical(&self) -> Self::CanonicalType {
 | 
			
		||||
        ArrayType {
 | 
			
		||||
            element: self.element.canonical_dyn(),
 | 
			
		||||
            len: self.len(),
 | 
			
		||||
            bit_width: self.bit_width,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn source_location(&self) -> SourceLocation {
 | 
			
		||||
        SourceLocation::builtin()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn type_enum(&self) -> TypeEnum {
 | 
			
		||||
        TypeEnum::ArrayType(self.canonical())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn from_canonical_type(t: Self::CanonicalType) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            element: VA::ElementType::from_dyn_canonical_type(t.element),
 | 
			
		||||
            len: VA::try_len_type_from_len(t.len).expect("length should match"),
 | 
			
		||||
            bit_width: t.bit_width,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> {
 | 
			
		||||
        Some(<dyn Any>::downcast_ref::<ArrayType<[DynCanonicalValue]>>(
 | 
			
		||||
            this,
 | 
			
		||||
        )?)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<Lhs: ValueArrayOrSlice + ?Sized, Rhs: ValueArrayOrSlice + ?Sized> Connect<ArrayType<Rhs>>
 | 
			
		||||
    for ArrayType<Lhs>
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl CanonicalType for ArrayType<[DynCanonicalValue]> {
 | 
			
		||||
    const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::ArrayType;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, PartialEq, Eq, Hash)]
 | 
			
		||||
pub struct Array<VA: ValueArrayOrSlice + ?Sized> {
 | 
			
		||||
    element_ty: VA::ElementType,
 | 
			
		||||
    value: Arc<VA>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<VA: ValueArrayOrSlice + ?Sized> Clone for Array<VA> {
 | 
			
		||||
    fn clone(&self) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            element_ty: self.element_ty.clone(),
 | 
			
		||||
            value: self.value.clone(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<VA: ValueArrayOrSlice + ?Sized> ToExpr for Array<VA> {
 | 
			
		||||
    type Type = ArrayType<VA>;
 | 
			
		||||
 | 
			
		||||
    fn ty(&self) -> Self::Type {
 | 
			
		||||
        ArrayType::new_with_len_type(self.element_ty.clone(), self.value.len_type())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
 | 
			
		||||
        Expr::from_value(self)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<VA: ValueArrayOrSlice + ?Sized> Value for Array<VA> {
 | 
			
		||||
    fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue {
 | 
			
		||||
        Array {
 | 
			
		||||
            element_ty: self.element_ty.canonical_dyn(),
 | 
			
		||||
            value: AsRef::<[_]>::as_ref(&*self.value)
 | 
			
		||||
                .iter()
 | 
			
		||||
                .map(|v| v.to_canonical_dyn())
 | 
			
		||||
                .collect(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
 | 
			
		||||
        #[derive(Hash, Eq, PartialEq)]
 | 
			
		||||
        struct ArrayToBitsMemoize<VA: ValueArrayOrSlice + ?Sized>(PhantomData<VA>);
 | 
			
		||||
        impl<VA: ValueArrayOrSlice + ?Sized> Clone for ArrayToBitsMemoize<VA> {
 | 
			
		||||
            fn clone(&self) -> Self {
 | 
			
		||||
                *self
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        impl<VA: ValueArrayOrSlice + ?Sized> Copy for ArrayToBitsMemoize<VA> {}
 | 
			
		||||
        impl<VA: ValueArrayOrSlice + ?Sized> Memoize for ArrayToBitsMemoize<VA> {
 | 
			
		||||
            type Input = Array<VA>;
 | 
			
		||||
            type InputOwned = Array<VA>;
 | 
			
		||||
            type Output = Interned<BitSlice>;
 | 
			
		||||
 | 
			
		||||
            fn inner(self, input: &Self::Input) -> Self::Output {
 | 
			
		||||
                let mut bits = BitVec::with_capacity(input.ty().bit_width());
 | 
			
		||||
                for element in AsRef::<[_]>::as_ref(&*input.value).iter() {
 | 
			
		||||
                    bits.extend_from_bitslice(&element.to_bits());
 | 
			
		||||
                }
 | 
			
		||||
                Intern::intern_owned(bits)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        ArrayToBitsMemoize::<VA>(PhantomData).get(this)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl CanonicalValue for Array<[DynCanonicalValue]> {
 | 
			
		||||
    fn value_enum_impl(this: &Self) -> ValueEnum {
 | 
			
		||||
        ValueEnum::Array(this.clone())
 | 
			
		||||
    }
 | 
			
		||||
    fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
 | 
			
		||||
        Value::to_bits_impl(this)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<VA: ValueArrayOrSlice + ?Sized> Array<VA> {
 | 
			
		||||
    pub fn element_ty(&self) -> &VA::ElementType {
 | 
			
		||||
        &self.element_ty
 | 
			
		||||
    }
 | 
			
		||||
    pub fn len(&self) -> usize {
 | 
			
		||||
        VA::len_from_len_type(self.value.len_type())
 | 
			
		||||
    }
 | 
			
		||||
    pub fn is_empty(&self) -> bool {
 | 
			
		||||
        self.len() == 0
 | 
			
		||||
    }
 | 
			
		||||
    pub fn value(&self) -> &Arc<VA> {
 | 
			
		||||
        &self.value
 | 
			
		||||
    }
 | 
			
		||||
    pub fn set_element(&mut self, index: usize, element: VA::Element) {
 | 
			
		||||
        assert_eq!(self.element_ty, element.ty());
 | 
			
		||||
        VA::arc_make_mut(&mut self.value)[index] = element;
 | 
			
		||||
    }
 | 
			
		||||
    pub fn new(element_ty: VA::ElementType, value: Arc<VA>) -> Self {
 | 
			
		||||
        for element in value.iter() {
 | 
			
		||||
            assert_eq!(element_ty, element.ty());
 | 
			
		||||
        }
 | 
			
		||||
        Self { element_ty, value }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn into_slice(self) -> Array<[VA::Element]> {
 | 
			
		||||
        Array {
 | 
			
		||||
            element_ty: self.element_ty,
 | 
			
		||||
            value: self.value.arc_to_arc_slice(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<VA: ValueArrayOrSlice + ?Sized, T: Into<Arc<VA>>> From<T> for Array<VA>
 | 
			
		||||
where
 | 
			
		||||
    VA::ElementType: FixedType,
 | 
			
		||||
{
 | 
			
		||||
    fn from(value: T) -> Self {
 | 
			
		||||
        Self::new(FixedType::fixed_type(), value.into())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<E: ToExpr<Type = T>, T: FixedType> ToExpr for [E] {
 | 
			
		||||
    type Type = ArrayType<[T::Value]>;
 | 
			
		||||
 | 
			
		||||
    fn ty(&self) -> Self::Type {
 | 
			
		||||
        ArrayType::new_with_len_type(FixedType::fixed_type(), self.len())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
 | 
			
		||||
        let elements = Intern::intern_owned(Vec::from_iter(
 | 
			
		||||
            self.iter().map(|v| v.to_expr().to_canonical_dyn()),
 | 
			
		||||
        ));
 | 
			
		||||
        ArrayLiteral::new_unchecked(elements, self.ty()).to_expr()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<E: ToExpr<Type = T>, T: FixedType> ToExpr for Vec<E> {
 | 
			
		||||
    type Type = ArrayType<[T::Value]>;
 | 
			
		||||
 | 
			
		||||
    fn ty(&self) -> Self::Type {
 | 
			
		||||
        <[E]>::ty(self)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
 | 
			
		||||
        <[E]>::to_expr(self)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<E: ToExpr<Type = T>, T: FixedType> ToExpr for [E; 0] {
 | 
			
		||||
    type Type = ArrayType<[T::Value; 0]>;
 | 
			
		||||
 | 
			
		||||
    fn ty(&self) -> Self::Type {
 | 
			
		||||
        ArrayType::new_with_len_type(FixedType::fixed_type(), ())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
 | 
			
		||||
        Array::new(FixedType::fixed_type(), Arc::new([])).to_expr()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
macro_rules! impl_to_expr_for_non_empty_array {
 | 
			
		||||
    ($N:literal) => {
 | 
			
		||||
        impl<E: ToExpr<Type = T>, T: Type> ToExpr for [E; $N] {
 | 
			
		||||
            type Type = ArrayType<[T::Value; $N]>;
 | 
			
		||||
 | 
			
		||||
            fn ty(&self) -> Self::Type {
 | 
			
		||||
                ArrayType::new_with_len_type(self[0].ty(), ())
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
 | 
			
		||||
                let elements = Intern::intern_owned(Vec::from_iter(
 | 
			
		||||
                    self.iter().map(|v| v.to_expr().to_canonical_dyn()),
 | 
			
		||||
                ));
 | 
			
		||||
                ArrayLiteral::new_unchecked(elements, self.ty()).to_expr()
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl_to_expr_for_non_empty_array!(1);
 | 
			
		||||
impl_to_expr_for_non_empty_array!(2);
 | 
			
		||||
impl_to_expr_for_non_empty_array!(3);
 | 
			
		||||
impl_to_expr_for_non_empty_array!(4);
 | 
			
		||||
impl_to_expr_for_non_empty_array!(5);
 | 
			
		||||
impl_to_expr_for_non_empty_array!(6);
 | 
			
		||||
impl_to_expr_for_non_empty_array!(7);
 | 
			
		||||
impl_to_expr_for_non_empty_array!(8);
 | 
			
		||||
impl_to_expr_for_non_empty_array!(9);
 | 
			
		||||
impl_to_expr_for_non_empty_array!(10);
 | 
			
		||||
impl_to_expr_for_non_empty_array!(11);
 | 
			
		||||
impl_to_expr_for_non_empty_array!(12);
 | 
			
		||||
impl_to_expr_for_non_empty_array!(13);
 | 
			
		||||
impl_to_expr_for_non_empty_array!(14);
 | 
			
		||||
impl_to_expr_for_non_empty_array!(15);
 | 
			
		||||
impl_to_expr_for_non_empty_array!(16);
 | 
			
		||||
impl_to_expr_for_non_empty_array!(17);
 | 
			
		||||
impl_to_expr_for_non_empty_array!(18);
 | 
			
		||||
impl_to_expr_for_non_empty_array!(19);
 | 
			
		||||
impl_to_expr_for_non_empty_array!(20);
 | 
			
		||||
impl_to_expr_for_non_empty_array!(21);
 | 
			
		||||
impl_to_expr_for_non_empty_array!(22);
 | 
			
		||||
impl_to_expr_for_non_empty_array!(23);
 | 
			
		||||
impl_to_expr_for_non_empty_array!(24);
 | 
			
		||||
impl_to_expr_for_non_empty_array!(25);
 | 
			
		||||
impl_to_expr_for_non_empty_array!(26);
 | 
			
		||||
impl_to_expr_for_non_empty_array!(27);
 | 
			
		||||
impl_to_expr_for_non_empty_array!(28);
 | 
			
		||||
impl_to_expr_for_non_empty_array!(29);
 | 
			
		||||
impl_to_expr_for_non_empty_array!(30);
 | 
			
		||||
impl_to_expr_for_non_empty_array!(31);
 | 
			
		||||
impl_to_expr_for_non_empty_array!(32);
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug)]
 | 
			
		||||
pub struct ArrayIntoIter<VA: ValueArrayOrSlice> {
 | 
			
		||||
    array: Arc<VA>,
 | 
			
		||||
    indexes: std::ops::Range<usize>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<VA: ValueArrayOrSlice> Iterator for ArrayIntoIter<VA> {
 | 
			
		||||
    type Item = VA::Element;
 | 
			
		||||
 | 
			
		||||
    fn next(&mut self) -> Option<Self::Item> {
 | 
			
		||||
        Some(self.array[self.indexes.next()?].clone())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn size_hint(&self) -> (usize, Option<usize>) {
 | 
			
		||||
        self.indexes.size_hint()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<VA: ValueArrayOrSlice> std::iter::FusedIterator for ArrayIntoIter<VA> {}
 | 
			
		||||
 | 
			
		||||
impl<VA: ValueArrayOrSlice> ExactSizeIterator for ArrayIntoIter<VA> {}
 | 
			
		||||
 | 
			
		||||
impl<VA: ValueArrayOrSlice> DoubleEndedIterator for ArrayIntoIter<VA> {
 | 
			
		||||
    fn next_back(&mut self) -> Option<Self::Item> {
 | 
			
		||||
        Some(self.array[self.indexes.next_back()?].clone())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<VA: ValueArrayOrSlice> Array<VA> {
 | 
			
		||||
    pub fn iter(&self) -> std::slice::Iter<'_, VA::Element> {
 | 
			
		||||
        self.value.iter()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a, VA: ValueArrayOrSlice> IntoIterator for &'a Array<VA> {
 | 
			
		||||
    type Item = &'a VA::Element;
 | 
			
		||||
    type IntoIter = std::slice::Iter<'a, VA::Element>;
 | 
			
		||||
 | 
			
		||||
    fn into_iter(self) -> Self::IntoIter {
 | 
			
		||||
        self.value.iter()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<VA: ValueArrayOrSlice> IntoIterator for Array<VA> {
 | 
			
		||||
    type Item = VA::Element;
 | 
			
		||||
    type IntoIter = ArrayIntoIter<VA>;
 | 
			
		||||
 | 
			
		||||
    fn into_iter(self) -> Self::IntoIter {
 | 
			
		||||
        ArrayIntoIter {
 | 
			
		||||
            indexes: 0..self.len(),
 | 
			
		||||
            array: self.value,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug)]
 | 
			
		||||
pub struct ArrayExprIter<VA: ValueArrayOrSlice> {
 | 
			
		||||
    array: Expr<Array<VA>>,
 | 
			
		||||
    indexes: std::ops::Range<usize>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<VA: ValueArrayOrSlice> Iterator for ArrayExprIter<VA> {
 | 
			
		||||
    type Item = Expr<VA::Element>;
 | 
			
		||||
 | 
			
		||||
    fn next(&mut self) -> Option<Self::Item> {
 | 
			
		||||
        Some(ExprIndex::expr_index(self.array, self.indexes.next()?))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn size_hint(&self) -> (usize, Option<usize>) {
 | 
			
		||||
        self.indexes.size_hint()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<VA: ValueArrayOrSlice> std::iter::FusedIterator for ArrayExprIter<VA> {}
 | 
			
		||||
 | 
			
		||||
impl<VA: ValueArrayOrSlice> ExactSizeIterator for ArrayExprIter<VA> {}
 | 
			
		||||
 | 
			
		||||
impl<VA: ValueArrayOrSlice> DoubleEndedIterator for ArrayExprIter<VA> {
 | 
			
		||||
    fn next_back(&mut self) -> Option<Self::Item> {
 | 
			
		||||
        Some(ExprIndex::expr_index(self.array, self.indexes.next_back()?))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<VA: ValueArrayOrSlice> IntoIterator for Expr<Array<VA>> {
 | 
			
		||||
    type Item = Expr<VA::Element>;
 | 
			
		||||
    type IntoIter = ArrayExprIter<VA>;
 | 
			
		||||
 | 
			
		||||
    fn into_iter(self) -> Self::IntoIter {
 | 
			
		||||
        self.iter()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<VA: ValueArrayOrSlice> IntoIterator for &'_ Expr<Array<VA>> {
 | 
			
		||||
    type Item = Expr<VA::Element>;
 | 
			
		||||
    type IntoIter = ArrayExprIter<VA>;
 | 
			
		||||
 | 
			
		||||
    fn into_iter(self) -> Self::IntoIter {
 | 
			
		||||
        self.iter()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<VA: ValueArrayOrSlice> Expr<Array<VA>> {
 | 
			
		||||
    pub fn len(self) -> usize {
 | 
			
		||||
        self.canonical_type().len()
 | 
			
		||||
    }
 | 
			
		||||
    pub fn is_empty(self) -> bool {
 | 
			
		||||
        self.canonical_type().is_empty()
 | 
			
		||||
    }
 | 
			
		||||
    pub fn iter(self) -> ArrayExprIter<VA> {
 | 
			
		||||
        ArrayExprIter {
 | 
			
		||||
            indexes: 0..self.len(),
 | 
			
		||||
            array: self,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										796
									
								
								crates/fayalite/src/bundle.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										796
									
								
								crates/fayalite/src/bundle.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,796 @@
 | 
			
		|||
// SPDX-License-Identifier: LGPL-3.0-or-later
 | 
			
		||||
// See Notices.txt for copyright information
 | 
			
		||||
use crate::{
 | 
			
		||||
    expr::{ops::BundleLiteral, Expr, ToExpr},
 | 
			
		||||
    intern::{
 | 
			
		||||
        Intern, Interned, InternedCompare, Memoize, PtrEqWithTypeId, SupportsPtrEqWithTypeId,
 | 
			
		||||
    },
 | 
			
		||||
    module::{ModuleBuilder, NormalModule},
 | 
			
		||||
    source_location::SourceLocation,
 | 
			
		||||
    ty::{
 | 
			
		||||
        CanonicalType, CanonicalTypeKind, CanonicalValue, Connect, DynCanonicalType,
 | 
			
		||||
        DynCanonicalValue, DynType, FixedType, MatchVariantWithoutScope, Type, TypeEnum,
 | 
			
		||||
        TypeWithDeref, Value, ValueEnum,
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
use bitvec::{slice::BitSlice, vec::BitVec};
 | 
			
		||||
use hashbrown::HashMap;
 | 
			
		||||
use std::{
 | 
			
		||||
    fmt,
 | 
			
		||||
    hash::{Hash, Hasher},
 | 
			
		||||
    marker::PhantomData,
 | 
			
		||||
    sync::Arc,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
 | 
			
		||||
pub struct FieldType<T> {
 | 
			
		||||
    pub name: Interned<str>,
 | 
			
		||||
    pub flipped: bool,
 | 
			
		||||
    pub ty: T,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct FmtDebugInStruct<'a, T> {
 | 
			
		||||
    field: &'a FieldType<T>,
 | 
			
		||||
    field_offset: usize,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: fmt::Debug> fmt::Debug for FmtDebugInStruct<'_, T> {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        let Self {
 | 
			
		||||
            field:
 | 
			
		||||
                &FieldType {
 | 
			
		||||
                    name,
 | 
			
		||||
                    flipped,
 | 
			
		||||
                    ref ty,
 | 
			
		||||
                },
 | 
			
		||||
            field_offset,
 | 
			
		||||
        } = *self;
 | 
			
		||||
        if flipped {
 | 
			
		||||
            write!(f, "#[hdl(flip)] ")?;
 | 
			
		||||
        }
 | 
			
		||||
        if f.alternate() {
 | 
			
		||||
            writeln!(f, "/* offset = {field_offset} */")?;
 | 
			
		||||
        }
 | 
			
		||||
        write!(f, "{name}: ")?;
 | 
			
		||||
        ty.fmt(f)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: fmt::Debug> fmt::Display for FmtDebugInStruct<'_, T> {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        fmt::Debug::fmt(self, f)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T> FieldType<T> {
 | 
			
		||||
    pub fn map_ty<U, F: FnOnce(T) -> U>(self, f: F) -> FieldType<U> {
 | 
			
		||||
        let Self { name, flipped, ty } = self;
 | 
			
		||||
        FieldType {
 | 
			
		||||
            name,
 | 
			
		||||
            flipped,
 | 
			
		||||
            ty: f(ty),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn as_ref_ty(&self) -> FieldType<&T> {
 | 
			
		||||
        FieldType {
 | 
			
		||||
            name: self.name,
 | 
			
		||||
            flipped: self.flipped,
 | 
			
		||||
            ty: &self.ty,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn fmt_debug_in_struct(&self, field_offset: usize) -> FmtDebugInStruct<'_, T> {
 | 
			
		||||
        FmtDebugInStruct {
 | 
			
		||||
            field: self,
 | 
			
		||||
            field_offset,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: Type> FieldType<T> {
 | 
			
		||||
    pub fn canonical(&self) -> FieldType<T::CanonicalType> {
 | 
			
		||||
        FieldType {
 | 
			
		||||
            name: self.name,
 | 
			
		||||
            flipped: self.flipped,
 | 
			
		||||
            ty: self.ty.canonical(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn to_dyn(&self) -> FieldType<Interned<dyn DynType>> {
 | 
			
		||||
        FieldType {
 | 
			
		||||
            name: self.name,
 | 
			
		||||
            flipped: self.flipped,
 | 
			
		||||
            ty: self.ty.to_dyn(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn canonical_dyn(&self) -> FieldType<Interned<dyn DynCanonicalType>> {
 | 
			
		||||
        FieldType {
 | 
			
		||||
            name: self.name,
 | 
			
		||||
            flipped: self.flipped,
 | 
			
		||||
            ty: self.ty.canonical_dyn(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl FieldType<Interned<dyn DynCanonicalType>> {
 | 
			
		||||
    pub fn from_canonical_type_helper<T: Type>(
 | 
			
		||||
        self,
 | 
			
		||||
        expected_name: &str,
 | 
			
		||||
        expected_flipped: bool,
 | 
			
		||||
    ) -> T {
 | 
			
		||||
        assert_eq!(&*self.name, expected_name, "field name doesn't match");
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            self.flipped, expected_flipped,
 | 
			
		||||
            "field {expected_name} orientation (flipped or not) doesn't match"
 | 
			
		||||
        );
 | 
			
		||||
        let ty = &*self.ty;
 | 
			
		||||
        if let Ok(ty) = <dyn DynCanonicalType>::downcast(ty) {
 | 
			
		||||
            return T::from_canonical_type(ty);
 | 
			
		||||
        }
 | 
			
		||||
        let type_name = std::any::type_name::<T::CanonicalType>();
 | 
			
		||||
        panic!("field {expected_name} type doesn't match, expected: {type_name:?}, got: {ty:?}");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Eq)]
 | 
			
		||||
struct DynBundleTypeImpl {
 | 
			
		||||
    fields: Interned<[FieldType<Interned<dyn DynCanonicalType>>]>,
 | 
			
		||||
    name_indexes: HashMap<Interned<str>, usize>,
 | 
			
		||||
    field_offsets: Interned<[usize]>,
 | 
			
		||||
    is_passive: bool,
 | 
			
		||||
    is_storable: bool,
 | 
			
		||||
    is_castable_from_bits: bool,
 | 
			
		||||
    bit_width: usize,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl fmt::Debug for DynBundleTypeImpl {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        write!(f, "DynBundleType ")?;
 | 
			
		||||
        f.debug_set()
 | 
			
		||||
            .entries(
 | 
			
		||||
                self.fields
 | 
			
		||||
                    .iter()
 | 
			
		||||
                    .enumerate()
 | 
			
		||||
                    .map(|(index, field)| field.fmt_debug_in_struct(self.field_offsets[index])),
 | 
			
		||||
            )
 | 
			
		||||
            .finish()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl PartialEq for DynBundleTypeImpl {
 | 
			
		||||
    fn eq(&self, other: &Self) -> bool {
 | 
			
		||||
        self.fields == other.fields
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Hash for DynBundleTypeImpl {
 | 
			
		||||
    fn hash<H: Hasher>(&self, state: &mut H) {
 | 
			
		||||
        self.fields.hash(state);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Copy, Clone, Hash, PartialEq, Eq)]
 | 
			
		||||
pub struct DynBundleType(Interned<DynBundleTypeImpl>);
 | 
			
		||||
 | 
			
		||||
impl fmt::Debug for DynBundleType {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        self.0.fmt(f)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl DynBundleType {
 | 
			
		||||
    pub fn new(fields: Interned<[FieldType<Interned<dyn DynCanonicalType>>]>) -> Self {
 | 
			
		||||
        let is_passive = fields
 | 
			
		||||
            .iter()
 | 
			
		||||
            .all(|field| !field.flipped && field.ty.is_passive());
 | 
			
		||||
        let is_storable = fields
 | 
			
		||||
            .iter()
 | 
			
		||||
            .all(|field| !field.flipped && field.ty.is_storable());
 | 
			
		||||
        let is_castable_from_bits = fields
 | 
			
		||||
            .iter()
 | 
			
		||||
            .all(|field| !field.flipped && field.ty.is_castable_from_bits());
 | 
			
		||||
        let mut name_indexes = HashMap::with_capacity(fields.len());
 | 
			
		||||
        let mut field_offsets = Vec::with_capacity(fields.len());
 | 
			
		||||
        let mut bit_width = 0usize;
 | 
			
		||||
        for (index, &FieldType { name, ty, .. }) in fields.iter().enumerate() {
 | 
			
		||||
            if let Some(old_index) = name_indexes.insert(name, index) {
 | 
			
		||||
                panic!("duplicate field name {name:?}: at both index {old_index} and {index}");
 | 
			
		||||
            }
 | 
			
		||||
            field_offsets.push(bit_width);
 | 
			
		||||
            bit_width = bit_width
 | 
			
		||||
                .checked_add(ty.bit_width())
 | 
			
		||||
                .unwrap_or_else(|| panic!("bundle is too big: bit-width overflowed"));
 | 
			
		||||
        }
 | 
			
		||||
        Self(
 | 
			
		||||
            DynBundleTypeImpl {
 | 
			
		||||
                fields,
 | 
			
		||||
                name_indexes,
 | 
			
		||||
                field_offsets: Intern::intern_owned(field_offsets),
 | 
			
		||||
                is_passive,
 | 
			
		||||
                is_storable,
 | 
			
		||||
                is_castable_from_bits,
 | 
			
		||||
                bit_width,
 | 
			
		||||
            }
 | 
			
		||||
            .intern_sized(),
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
    pub fn is_passive(self) -> bool {
 | 
			
		||||
        self.0.is_passive
 | 
			
		||||
    }
 | 
			
		||||
    pub fn is_storable(self) -> bool {
 | 
			
		||||
        self.0.is_storable
 | 
			
		||||
    }
 | 
			
		||||
    pub fn is_castable_from_bits(self) -> bool {
 | 
			
		||||
        self.0.is_castable_from_bits
 | 
			
		||||
    }
 | 
			
		||||
    pub fn bit_width(self) -> usize {
 | 
			
		||||
        self.0.bit_width
 | 
			
		||||
    }
 | 
			
		||||
    pub fn name_indexes(&self) -> &HashMap<Interned<str>, usize> {
 | 
			
		||||
        &self.0.name_indexes
 | 
			
		||||
    }
 | 
			
		||||
    pub fn field_by_name(
 | 
			
		||||
        &self,
 | 
			
		||||
        name: Interned<str>,
 | 
			
		||||
    ) -> Option<FieldType<Interned<dyn DynCanonicalType>>> {
 | 
			
		||||
        Some(self.0.fields[*self.0.name_indexes.get(&name)?])
 | 
			
		||||
    }
 | 
			
		||||
    pub fn field_offsets(self) -> Interned<[usize]> {
 | 
			
		||||
        self.0.field_offsets
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
 | 
			
		||||
pub struct DynBundle {
 | 
			
		||||
    ty: DynBundleType,
 | 
			
		||||
    fields: Arc<[DynCanonicalValue]>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl DynBundle {
 | 
			
		||||
    pub fn new(ty: DynBundleType, fields: Arc<[DynCanonicalValue]>) -> Self {
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            ty.fields().len(),
 | 
			
		||||
            fields.len(),
 | 
			
		||||
            "field values don't match type"
 | 
			
		||||
        );
 | 
			
		||||
        for (field_ty, field) in ty.fields().iter().zip(fields.iter()) {
 | 
			
		||||
            assert_eq!(field_ty.ty, field.ty(), "field value doesn't match type");
 | 
			
		||||
        }
 | 
			
		||||
        DynBundle { ty, fields }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn fields(&self) -> &Arc<[DynCanonicalValue]> {
 | 
			
		||||
        &self.fields
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub trait TypeHintTrait: Send + Sync + fmt::Debug + SupportsPtrEqWithTypeId {
 | 
			
		||||
    fn matches(&self, ty: &dyn DynType) -> Result<(), String>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl InternedCompare for dyn TypeHintTrait {
 | 
			
		||||
    type InternedCompareKey = PtrEqWithTypeId;
 | 
			
		||||
    fn interned_compare_key_ref(this: &Self) -> Self::InternedCompareKey {
 | 
			
		||||
        Self::get_ptr_eq_with_type_id(this)
 | 
			
		||||
    }
 | 
			
		||||
    fn interned_compare_key_weak(this: &std::sync::Weak<Self>) -> Self::InternedCompareKey {
 | 
			
		||||
        Self::get_ptr_eq_with_type_id(&*this.upgrade().unwrap())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct TypeHint<T: Type>(PhantomData<fn(T)>);
 | 
			
		||||
 | 
			
		||||
impl<T: Type> TypeHint<T> {
 | 
			
		||||
    pub fn intern_dyn() -> Interned<dyn TypeHintTrait> {
 | 
			
		||||
        Interned::cast_unchecked(
 | 
			
		||||
            Self(PhantomData).intern_sized(),
 | 
			
		||||
            |v| -> &dyn TypeHintTrait { v },
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: Type> fmt::Debug for TypeHint<T> {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        write!(f, "TypeHint<{}>", std::any::type_name::<T>())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: Type + Hash> Hash for TypeHint<T> {
 | 
			
		||||
    fn hash<H: Hasher>(&self, _state: &mut H) {}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: Type> Eq for TypeHint<T> {}
 | 
			
		||||
 | 
			
		||||
impl<T: Type> PartialEq for TypeHint<T> {
 | 
			
		||||
    fn eq(&self, _other: &Self) -> bool {
 | 
			
		||||
        true
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: Type> Clone for TypeHint<T> {
 | 
			
		||||
    fn clone(&self) -> Self {
 | 
			
		||||
        *self
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: Type> Copy for TypeHint<T> {}
 | 
			
		||||
 | 
			
		||||
impl<T: Type> TypeHintTrait for TypeHint<T> {
 | 
			
		||||
    fn matches(&self, ty: &dyn DynType) -> Result<(), String> {
 | 
			
		||||
        match ty.downcast::<T>() {
 | 
			
		||||
            Ok(_) => Ok(()),
 | 
			
		||||
            Err(_) => Err(format!("can't cast {ty:?} to {self:?}")),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
 | 
			
		||||
pub struct FieldsHint {
 | 
			
		||||
    pub known_fields: Interned<[FieldType<Interned<dyn TypeHintTrait>>]>,
 | 
			
		||||
    pub more_fields: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl FieldsHint {
 | 
			
		||||
    pub fn new(
 | 
			
		||||
        known_fields: impl IntoIterator<Item = FieldType<Interned<dyn TypeHintTrait>>>,
 | 
			
		||||
        more_fields: bool,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        let known_fields = Intern::intern_owned(Vec::from_iter(known_fields));
 | 
			
		||||
        Self {
 | 
			
		||||
            known_fields,
 | 
			
		||||
            more_fields,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn check_field(self, index: usize, field: FieldType<&dyn DynType>) -> Result<(), String> {
 | 
			
		||||
        let Some(&known_field) = self.known_fields.get(index) else {
 | 
			
		||||
            return if self.more_fields {
 | 
			
		||||
                Ok(())
 | 
			
		||||
            } else {
 | 
			
		||||
                Err(format!(
 | 
			
		||||
                    "too many fields: name={:?} index={index}",
 | 
			
		||||
                    field.name
 | 
			
		||||
                ))
 | 
			
		||||
            };
 | 
			
		||||
        };
 | 
			
		||||
        let FieldType {
 | 
			
		||||
            name: known_name,
 | 
			
		||||
            flipped: known_flipped,
 | 
			
		||||
            ty: type_hint,
 | 
			
		||||
        } = known_field;
 | 
			
		||||
        let FieldType { name, flipped, ty } = field;
 | 
			
		||||
        if name != known_name {
 | 
			
		||||
            Err(format!(
 | 
			
		||||
                "wrong field name {name:?}, expected {known_name:?}"
 | 
			
		||||
            ))
 | 
			
		||||
        } else if flipped != known_flipped {
 | 
			
		||||
            Err(format!(
 | 
			
		||||
                "wrong field direction: flipped={flipped:?}, expected flipped={known_flipped}"
 | 
			
		||||
            ))
 | 
			
		||||
        } else {
 | 
			
		||||
            type_hint.matches(ty)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub trait BundleType:
 | 
			
		||||
    Type<CanonicalType = DynBundleType, CanonicalValue = DynBundle> + TypeWithDeref + Connect<Self>
 | 
			
		||||
where
 | 
			
		||||
    Self::Value: BundleValue + ToExpr<Type = Self>,
 | 
			
		||||
{
 | 
			
		||||
    type Builder;
 | 
			
		||||
    fn builder() -> Self::Builder;
 | 
			
		||||
    fn fields(&self) -> Interned<[FieldType<Interned<dyn DynCanonicalType>>]>;
 | 
			
		||||
    fn fields_hint() -> FieldsHint;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub trait BundleValue: Value
 | 
			
		||||
where
 | 
			
		||||
    <Self as ToExpr>::Type: BundleType<Value = Self>,
 | 
			
		||||
{
 | 
			
		||||
    fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
 | 
			
		||||
        #[derive(Hash, Eq, PartialEq)]
 | 
			
		||||
        struct ToBitsMemoize<T>(PhantomData<T>);
 | 
			
		||||
        impl<T> Clone for ToBitsMemoize<T> {
 | 
			
		||||
            fn clone(&self) -> Self {
 | 
			
		||||
                *self
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        impl<T> Copy for ToBitsMemoize<T> {}
 | 
			
		||||
        impl<T: BundleValue<Type: BundleType<Value = T>>> Memoize for ToBitsMemoize<T> {
 | 
			
		||||
            type Input = T;
 | 
			
		||||
            type InputOwned = T;
 | 
			
		||||
            type Output = Interned<BitSlice>;
 | 
			
		||||
 | 
			
		||||
            fn inner(self, input: &Self::Input) -> Self::Output {
 | 
			
		||||
                let input = input.to_canonical();
 | 
			
		||||
                let mut bits = BitVec::with_capacity(input.ty.bit_width());
 | 
			
		||||
                for field in input.fields.iter() {
 | 
			
		||||
                    bits.extend_from_bitslice(&field.to_bits());
 | 
			
		||||
                }
 | 
			
		||||
                Intern::intern_owned(bits)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        ToBitsMemoize::<Self>(PhantomData).get(this)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct DynBundleMatch;
 | 
			
		||||
 | 
			
		||||
impl Type for DynBundleType {
 | 
			
		||||
    type CanonicalType = DynBundleType;
 | 
			
		||||
    type Value = DynBundle;
 | 
			
		||||
    type CanonicalValue = DynBundle;
 | 
			
		||||
    type MaskType = DynBundleType;
 | 
			
		||||
    type MaskValue = DynBundle;
 | 
			
		||||
    type MatchVariant = DynBundleMatch;
 | 
			
		||||
    type MatchActiveScope = ();
 | 
			
		||||
    type MatchVariantAndInactiveScope = MatchVariantWithoutScope<Self::MatchVariant>;
 | 
			
		||||
    type MatchVariantsIter = std::iter::Once<Self::MatchVariantAndInactiveScope>;
 | 
			
		||||
 | 
			
		||||
    fn match_variants<IO: BundleValue>(
 | 
			
		||||
        this: Expr<Self::Value>,
 | 
			
		||||
        module_builder: &mut ModuleBuilder<IO, NormalModule>,
 | 
			
		||||
        source_location: SourceLocation,
 | 
			
		||||
    ) -> Self::MatchVariantsIter
 | 
			
		||||
    where
 | 
			
		||||
        IO::Type: BundleType<Value = IO>,
 | 
			
		||||
    {
 | 
			
		||||
        let _ = this;
 | 
			
		||||
        let _ = module_builder;
 | 
			
		||||
        let _ = source_location;
 | 
			
		||||
        std::iter::once(MatchVariantWithoutScope(DynBundleMatch))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn mask_type(&self) -> Self::MaskType {
 | 
			
		||||
        #[derive(Copy, Clone, Eq, PartialEq, Hash)]
 | 
			
		||||
        struct Impl;
 | 
			
		||||
 | 
			
		||||
        impl Memoize for Impl {
 | 
			
		||||
            type Input = DynBundleType;
 | 
			
		||||
            type InputOwned = DynBundleType;
 | 
			
		||||
            type Output = DynBundleType;
 | 
			
		||||
 | 
			
		||||
            fn inner(self, input: &Self::Input) -> Self::Output {
 | 
			
		||||
                DynBundleType::new(Intern::intern_owned(Vec::from_iter(
 | 
			
		||||
                    input
 | 
			
		||||
                        .fields()
 | 
			
		||||
                        .iter()
 | 
			
		||||
                        .map(|&FieldType { name, flipped, ty }| FieldType {
 | 
			
		||||
                            name,
 | 
			
		||||
                            flipped,
 | 
			
		||||
                            ty: ty.mask_type().canonical(),
 | 
			
		||||
                        }),
 | 
			
		||||
                )))
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        Impl.get(self)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn canonical(&self) -> Self::CanonicalType {
 | 
			
		||||
        *self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn source_location(&self) -> SourceLocation {
 | 
			
		||||
        SourceLocation::builtin()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn type_enum(&self) -> TypeEnum {
 | 
			
		||||
        TypeEnum::BundleType(*self)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn from_canonical_type(t: Self::CanonicalType) -> Self {
 | 
			
		||||
        t
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> {
 | 
			
		||||
        Some(this)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct NoBuilder;
 | 
			
		||||
 | 
			
		||||
impl TypeWithDeref for DynBundleType {
 | 
			
		||||
    fn expr_deref(this: &Expr<Self::Value>) -> &Self::MatchVariant {
 | 
			
		||||
        let _ = this;
 | 
			
		||||
        &DynBundleMatch
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Connect<Self> for DynBundleType {}
 | 
			
		||||
 | 
			
		||||
impl BundleType for DynBundleType {
 | 
			
		||||
    type Builder = NoBuilder;
 | 
			
		||||
 | 
			
		||||
    fn builder() -> Self::Builder {
 | 
			
		||||
        NoBuilder
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn fields(&self) -> Interned<[FieldType<Interned<dyn DynCanonicalType>>]> {
 | 
			
		||||
        self.0.fields
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn fields_hint() -> FieldsHint {
 | 
			
		||||
        FieldsHint {
 | 
			
		||||
            known_fields: [][..].intern(),
 | 
			
		||||
            more_fields: true,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl CanonicalType for DynBundleType {
 | 
			
		||||
    const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::BundleType;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ToExpr for DynBundle {
 | 
			
		||||
    type Type = DynBundleType;
 | 
			
		||||
 | 
			
		||||
    fn ty(&self) -> Self::Type {
 | 
			
		||||
        self.ty
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
 | 
			
		||||
        Expr::from_value(self)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Value for DynBundle {
 | 
			
		||||
    fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue {
 | 
			
		||||
        self.clone()
 | 
			
		||||
    }
 | 
			
		||||
    fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
 | 
			
		||||
        BundleValue::to_bits_impl(this)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl BundleValue for DynBundle {}
 | 
			
		||||
 | 
			
		||||
impl CanonicalValue for DynBundle {
 | 
			
		||||
    fn value_enum_impl(this: &Self) -> ValueEnum {
 | 
			
		||||
        ValueEnum::Bundle(this.clone())
 | 
			
		||||
    }
 | 
			
		||||
    fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
 | 
			
		||||
        BundleValue::to_bits_impl(this)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
macro_rules! impl_tuple_builder {
 | 
			
		||||
    ($builder:ident, [
 | 
			
		||||
        $(($before_Ts:ident $before_fields:ident $before_members:literal))*
 | 
			
		||||
    ] [
 | 
			
		||||
        ($T:ident $field:ident $m:literal)
 | 
			
		||||
        $(($after_Ts:ident $after_fields:ident $after_members:literal))*
 | 
			
		||||
    ]) => {
 | 
			
		||||
        impl_tuple_builder!($builder, [
 | 
			
		||||
            $(($before_Ts $before_fields $before_members))*
 | 
			
		||||
            ($T $field $m)
 | 
			
		||||
        ] [
 | 
			
		||||
            $(($after_Ts $after_fields $after_members))*
 | 
			
		||||
        ]);
 | 
			
		||||
 | 
			
		||||
        impl<Phantom, $($before_Ts,)* $($after_Ts,)*> $builder<Phantom, $($before_Ts,)* () $(, $after_Ts)*> {
 | 
			
		||||
            pub fn $field<$T: ToExpr>(self, $field: $T) -> $builder<Phantom, $($before_Ts,)* Expr<<$T::Type as Type>::Value> $(, $after_Ts)*> {
 | 
			
		||||
                let Self {
 | 
			
		||||
                    $($before_fields,)*
 | 
			
		||||
                    $field: _,
 | 
			
		||||
                    $($after_fields, )*
 | 
			
		||||
                    _phantom: _,
 | 
			
		||||
                } = self;
 | 
			
		||||
                let $field = $field.to_expr();
 | 
			
		||||
                $builder {
 | 
			
		||||
                    $($before_fields,)*
 | 
			
		||||
                    $field,
 | 
			
		||||
                    $($after_fields,)*
 | 
			
		||||
                    _phantom: PhantomData,
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
    ($builder:ident, [$($before:tt)*] []) => {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
macro_rules! into_unit {
 | 
			
		||||
    ($($tt:tt)*) => {
 | 
			
		||||
        ()
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
macro_rules! impl_tuple {
 | 
			
		||||
    ($builder:ident, $(($T:ident $T2:ident $field:ident $m:tt)),*) => {
 | 
			
		||||
        pub struct $builder<Phantom, $($T),*> {
 | 
			
		||||
            $($field: $T,)*
 | 
			
		||||
            _phantom: PhantomData<Phantom>,
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        impl_tuple_builder!($builder, [] [$(($T $field $m))*]);
 | 
			
		||||
 | 
			
		||||
        impl<$($T: Value),*> $builder<($($T,)*), $(Expr<$T>,)*>
 | 
			
		||||
        where
 | 
			
		||||
            $($T::Type: Type<Value = $T>,)*
 | 
			
		||||
        {
 | 
			
		||||
            pub fn build(self) -> Expr<($($T,)*)> {
 | 
			
		||||
                let Self {
 | 
			
		||||
                    $($field,)*
 | 
			
		||||
                    _phantom: _,
 | 
			
		||||
                } = self;
 | 
			
		||||
                BundleLiteral::new_unchecked(
 | 
			
		||||
                    [$($field.to_canonical_dyn()),*][..].intern(),
 | 
			
		||||
                    ($($field.ty(),)*),
 | 
			
		||||
                ).to_expr()
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        impl<$($T: ToExpr,)*> ToExpr for ($($T,)*) {
 | 
			
		||||
            type Type = ($($T::Type,)*);
 | 
			
		||||
 | 
			
		||||
            #[allow(clippy::unused_unit)]
 | 
			
		||||
            fn ty(&self) -> Self::Type {
 | 
			
		||||
                let ($($field,)*) = self;
 | 
			
		||||
                ($($field.ty(),)*)
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
 | 
			
		||||
                let ($($field,)*) = self;
 | 
			
		||||
                $(let $field = $field.to_expr();)*
 | 
			
		||||
                BundleLiteral::new_unchecked(
 | 
			
		||||
                    [$($field.to_canonical_dyn()),*][..].intern(),
 | 
			
		||||
                    ($($field.ty(),)*),
 | 
			
		||||
                ).to_expr()
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        impl<$($T, $T2,)*> Connect<($($T2,)*)> for ($($T,)*)
 | 
			
		||||
        where
 | 
			
		||||
            $($T: Connect<$T2>,)*
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        impl<$($T: Type,)*> Type for ($($T,)*)
 | 
			
		||||
        where
 | 
			
		||||
            $($T::Value: Value<Type = $T>,)*
 | 
			
		||||
        {
 | 
			
		||||
            type CanonicalType = DynBundleType;
 | 
			
		||||
            type Value = ($($T::Value,)*);
 | 
			
		||||
            type CanonicalValue = DynBundle;
 | 
			
		||||
            type MaskType = ($($T::MaskType,)*);
 | 
			
		||||
            type MaskValue = ($($T::MaskValue,)*);
 | 
			
		||||
            type MatchVariant = ($(Expr<$T::Value>,)*);
 | 
			
		||||
            type MatchActiveScope = ();
 | 
			
		||||
            type MatchVariantAndInactiveScope = MatchVariantWithoutScope<Self::MatchVariant>;
 | 
			
		||||
            type MatchVariantsIter = std::iter::Once<Self::MatchVariantAndInactiveScope>;
 | 
			
		||||
 | 
			
		||||
            fn match_variants<IO: BundleValue>(
 | 
			
		||||
                this: Expr<Self::Value>,
 | 
			
		||||
                module_builder: &mut ModuleBuilder<IO, NormalModule>,
 | 
			
		||||
                source_location: SourceLocation,
 | 
			
		||||
            ) -> Self::MatchVariantsIter
 | 
			
		||||
            where
 | 
			
		||||
                IO::Type: BundleType<Value = IO>,
 | 
			
		||||
            {
 | 
			
		||||
                let _ = this;
 | 
			
		||||
                let _ = module_builder;
 | 
			
		||||
                let _ = source_location;
 | 
			
		||||
                std::iter::once(MatchVariantWithoutScope(($(this.field(stringify!($m)),)*)))
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            #[allow(clippy::unused_unit)]
 | 
			
		||||
            fn mask_type(&self) -> Self::MaskType {
 | 
			
		||||
                let ($($field,)*) = self;
 | 
			
		||||
                ($($field.mask_type(),)*)
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            fn canonical(&self) -> Self::CanonicalType {
 | 
			
		||||
                DynBundleType::new(self.fields())
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            fn source_location(&self) -> SourceLocation {
 | 
			
		||||
                SourceLocation::builtin()
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            fn type_enum(&self) -> TypeEnum {
 | 
			
		||||
                TypeEnum::BundleType(self.canonical())
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            #[allow(clippy::unused_unit)]
 | 
			
		||||
            fn from_canonical_type(t: Self::CanonicalType) -> Self {
 | 
			
		||||
                let [$($field),*] = *t.fields() else {
 | 
			
		||||
                    panic!("wrong number of fields");
 | 
			
		||||
                };
 | 
			
		||||
                ($($field.from_canonical_type_helper(stringify!($m), false),)*)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        impl<$($T: Type,)*> TypeWithDeref for ($($T,)*)
 | 
			
		||||
        where
 | 
			
		||||
            $($T::Value: Value<Type = $T>,)*
 | 
			
		||||
        {
 | 
			
		||||
            fn expr_deref(
 | 
			
		||||
                this: &::fayalite::expr::Expr<<Self as ::fayalite::ty::Type>::Value>,
 | 
			
		||||
            ) -> &<Self as ::fayalite::ty::Type>::MatchVariant {
 | 
			
		||||
                let _ = this;
 | 
			
		||||
                Interned::<_>::into_inner(
 | 
			
		||||
                    Intern::intern_sized((
 | 
			
		||||
                        $(this.field(stringify!($m)),)*
 | 
			
		||||
                    )),
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        impl<$($T: Type,)*> BundleType for ($($T,)*)
 | 
			
		||||
        where
 | 
			
		||||
            $($T::Value: Value<Type = $T>,)*
 | 
			
		||||
        {
 | 
			
		||||
            type Builder = $builder<($($T::Value,)*), $(into_unit!($T),)*>;
 | 
			
		||||
            fn builder() -> Self::Builder {
 | 
			
		||||
                $builder {
 | 
			
		||||
                    $($field: (),)*
 | 
			
		||||
                    _phantom: PhantomData,
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            fn fields(
 | 
			
		||||
                &self,
 | 
			
		||||
            ) -> Interned<[FieldType<Interned<dyn DynCanonicalType>>]> {
 | 
			
		||||
                [
 | 
			
		||||
                    $(FieldType {
 | 
			
		||||
                        name: stringify!($m).intern(),
 | 
			
		||||
                        flipped: false,
 | 
			
		||||
                        ty: self.$m.canonical_dyn(),
 | 
			
		||||
                    },)*
 | 
			
		||||
                ][..].intern()
 | 
			
		||||
            }
 | 
			
		||||
            fn fields_hint() -> FieldsHint {
 | 
			
		||||
                FieldsHint::new([
 | 
			
		||||
                    $(FieldType {
 | 
			
		||||
                        name: stringify!($m).intern(),
 | 
			
		||||
                        flipped: false,
 | 
			
		||||
                        ty: TypeHint::<$T>::intern_dyn(),
 | 
			
		||||
                    },)*
 | 
			
		||||
                ], false)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        impl<$($T: FixedType,)*> FixedType for ($($T,)*)
 | 
			
		||||
        where
 | 
			
		||||
            $($T::Value: Value<Type = $T>,)*
 | 
			
		||||
        {
 | 
			
		||||
            #[allow(clippy::unused_unit)]
 | 
			
		||||
            fn fixed_type() -> Self {
 | 
			
		||||
                ($($T::fixed_type(),)*)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        impl<$($T: Value,)*> Value for ($($T,)*)
 | 
			
		||||
        where
 | 
			
		||||
            $($T::Type: Type<Value = $T>,)*
 | 
			
		||||
        {
 | 
			
		||||
            fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue {
 | 
			
		||||
                let ty = self.ty().canonical();
 | 
			
		||||
                DynBundle::new(
 | 
			
		||||
                    ty,
 | 
			
		||||
                    Arc::new([
 | 
			
		||||
                        $(self.$m.to_canonical_dyn(),)*
 | 
			
		||||
                    ]),
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
            fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
 | 
			
		||||
                BundleValue::to_bits_impl(this)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        impl<$($T: Value,)*> BundleValue for ($($T,)*)
 | 
			
		||||
        where
 | 
			
		||||
            $($T::Type: Type<Value = $T>,)*
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl_tuple!(TupleBuilder0,);
 | 
			
		||||
impl_tuple!(TupleBuilder1, (A A2 field_0 0));
 | 
			
		||||
impl_tuple!(TupleBuilder2, (A A2 field_0 0), (B B2 field_1 1));
 | 
			
		||||
impl_tuple!(TupleBuilder3, (A A2 field_0 0), (B B2 field_1 1), (C C2 field_2 2));
 | 
			
		||||
impl_tuple!(TupleBuilder4, (A A2 field_0 0), (B B2 field_1 1), (C C2 field_2 2), (D D2 field_3 3));
 | 
			
		||||
impl_tuple!(TupleBuilder5, (A A2 field_0 0), (B B2 field_1 1), (C C2 field_2 2), (D D2 field_3 3), (E E2 field_4 4));
 | 
			
		||||
impl_tuple!(TupleBuilder6, (A A2 field_0 0), (B B2 field_1 1), (C C2 field_2 2), (D D2 field_3 3), (E E2 field_4 4), (F F2 field_5 5));
 | 
			
		||||
impl_tuple!(TupleBuilder7, (A A2 field_0 0), (B B2 field_1 1), (C C2 field_2 2), (D D2 field_3 3), (E E2 field_4 4), (F F2 field_5 5), (G G2 field_6 6));
 | 
			
		||||
impl_tuple!(TupleBuilder8, (A A2 field_0 0), (B B2 field_1 1), (C C2 field_2 2), (D D2 field_3 3), (E E2 field_4 4), (F F2 field_5 5), (G G2 field_6 6), (H H2 field_7 7));
 | 
			
		||||
impl_tuple!(TupleBuilder9, (A A2 field_0 0), (B B2 field_1 1), (C C2 field_2 2), (D D2 field_3 3), (E E2 field_4 4), (F F2 field_5 5), (G G2 field_6 6), (H H2 field_7 7), (I I2 field_8 8));
 | 
			
		||||
impl_tuple!(TupleBuilder10, (A A2 field_0 0), (B B2 field_1 1), (C C2 field_2 2), (D D2 field_3 3), (E E2 field_4 4), (F F2 field_5 5), (G G2 field_6 6), (H H2 field_7 7), (I I2 field_8 8), (J J2 field_9 9));
 | 
			
		||||
impl_tuple!(TupleBuilder11, (A A2 field_0 0), (B B2 field_1 1), (C C2 field_2 2), (D D2 field_3 3), (E E2 field_4 4), (F F2 field_5 5), (G G2 field_6 6), (H H2 field_7 7), (I I2 field_8 8), (J J2 field_9 9), (K K2 field_10 10));
 | 
			
		||||
impl_tuple!(TupleBuilder12, (A A2 field_0 0), (B B2 field_1 1), (C C2 field_2 2), (D D2 field_3 3), (E E2 field_4 4), (F F2 field_5 5), (G G2 field_6 6), (H H2 field_7 7), (I I2 field_8 8), (J J2 field_9 9), (K K2 field_10 10), (L L2 field_11 11));
 | 
			
		||||
							
								
								
									
										156
									
								
								crates/fayalite/src/clock.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										156
									
								
								crates/fayalite/src/clock.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,156 @@
 | 
			
		|||
// SPDX-License-Identifier: LGPL-3.0-or-later
 | 
			
		||||
// See Notices.txt for copyright information
 | 
			
		||||
use crate::{
 | 
			
		||||
    expr::{Expr, ToExpr},
 | 
			
		||||
    int::{UInt, UIntType},
 | 
			
		||||
    intern::Interned,
 | 
			
		||||
    reset::Reset,
 | 
			
		||||
    source_location::SourceLocation,
 | 
			
		||||
    ty::{
 | 
			
		||||
        impl_match_values_as_self, CanonicalType, CanonicalTypeKind, CanonicalValue, Connect,
 | 
			
		||||
        DynCanonicalType, FixedType, Type, TypeEnum, Value, ValueEnum,
 | 
			
		||||
    },
 | 
			
		||||
    util::interned_bit,
 | 
			
		||||
};
 | 
			
		||||
use bitvec::slice::BitSlice;
 | 
			
		||||
 | 
			
		||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)]
 | 
			
		||||
pub struct ClockType;
 | 
			
		||||
 | 
			
		||||
impl ClockType {
 | 
			
		||||
    pub const fn new() -> Self {
 | 
			
		||||
        Self
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Type for ClockType {
 | 
			
		||||
    type Value = Clock;
 | 
			
		||||
    type CanonicalType = ClockType;
 | 
			
		||||
    type CanonicalValue = Clock;
 | 
			
		||||
    type MaskType = UIntType<1>;
 | 
			
		||||
    type MaskValue = UInt<1>;
 | 
			
		||||
 | 
			
		||||
    impl_match_values_as_self!();
 | 
			
		||||
 | 
			
		||||
    fn mask_type(&self) -> Self::MaskType {
 | 
			
		||||
        UIntType::new()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn type_enum(&self) -> TypeEnum {
 | 
			
		||||
        TypeEnum::Clock(*self)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn from_canonical_type(t: Self::CanonicalType) -> Self {
 | 
			
		||||
        t
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn canonical(&self) -> Self::CanonicalType {
 | 
			
		||||
        *self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn source_location(&self) -> SourceLocation {
 | 
			
		||||
        SourceLocation::builtin()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> {
 | 
			
		||||
        Some(this)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Connect<Self> for ClockType {}
 | 
			
		||||
 | 
			
		||||
impl CanonicalType for ClockType {
 | 
			
		||||
    const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::Clock;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl FixedType for ClockType {
 | 
			
		||||
    fn fixed_type() -> Self {
 | 
			
		||||
        Self
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
 | 
			
		||||
pub struct Clock(pub bool);
 | 
			
		||||
 | 
			
		||||
impl ToExpr for Clock {
 | 
			
		||||
    type Type = ClockType;
 | 
			
		||||
 | 
			
		||||
    fn ty(&self) -> Self::Type {
 | 
			
		||||
        ClockType
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn to_expr(&self) -> Expr<Self> {
 | 
			
		||||
        Expr::from_value(self)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Value for Clock {
 | 
			
		||||
    fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue {
 | 
			
		||||
        *self
 | 
			
		||||
    }
 | 
			
		||||
    fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
 | 
			
		||||
        interned_bit(this.0)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl CanonicalValue for Clock {
 | 
			
		||||
    fn value_enum_impl(this: &Self) -> ValueEnum {
 | 
			
		||||
        ValueEnum::Clock(*this)
 | 
			
		||||
    }
 | 
			
		||||
    fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
 | 
			
		||||
        interned_bit(this.0)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, crate::Value)]
 | 
			
		||||
#[hdl(fixed_type, outline_generated)]
 | 
			
		||||
pub struct ClockDomain {
 | 
			
		||||
    pub clock: Clock,
 | 
			
		||||
    pub reset: Reset,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub trait ToClock {
 | 
			
		||||
    fn to_clock(&self) -> Expr<Clock>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: ?Sized + ToClock> ToClock for &'_ T {
 | 
			
		||||
    fn to_clock(&self) -> Expr<Clock> {
 | 
			
		||||
        (**self).to_clock()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: ?Sized + ToClock> ToClock for &'_ mut T {
 | 
			
		||||
    fn to_clock(&self) -> Expr<Clock> {
 | 
			
		||||
        (**self).to_clock()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: ?Sized + ToClock> ToClock for Box<T> {
 | 
			
		||||
    fn to_clock(&self) -> Expr<Clock> {
 | 
			
		||||
        (**self).to_clock()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ToClock for Expr<Clock> {
 | 
			
		||||
    fn to_clock(&self) -> Expr<Clock> {
 | 
			
		||||
        *self
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ToClock for Clock {
 | 
			
		||||
    fn to_clock(&self) -> Expr<Clock> {
 | 
			
		||||
        self.to_expr()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ToClock for bool {
 | 
			
		||||
    fn to_clock(&self) -> Expr<Clock> {
 | 
			
		||||
        self.to_expr().to_clock()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ToClock for UInt<1> {
 | 
			
		||||
    fn to_clock(&self) -> Expr<Clock> {
 | 
			
		||||
        self.to_expr().to_clock()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										620
									
								
								crates/fayalite/src/enum_.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										620
									
								
								crates/fayalite/src/enum_.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,620 @@
 | 
			
		|||
// SPDX-License-Identifier: LGPL-3.0-or-later
 | 
			
		||||
// See Notices.txt for copyright information
 | 
			
		||||
#![allow(clippy::type_complexity)]
 | 
			
		||||
use crate::{
 | 
			
		||||
    bundle::{BundleValue, TypeHintTrait},
 | 
			
		||||
    expr::{ops::VariantAccess, Expr, ToExpr},
 | 
			
		||||
    int::{UInt, UIntType},
 | 
			
		||||
    intern::{Intern, Interned, MemoizeGeneric},
 | 
			
		||||
    module::{
 | 
			
		||||
        EnumMatchVariantAndInactiveScopeImpl, EnumMatchVariantsIterImpl, ModuleBuilder,
 | 
			
		||||
        NormalModule, Scope,
 | 
			
		||||
    },
 | 
			
		||||
    source_location::SourceLocation,
 | 
			
		||||
    ty::{
 | 
			
		||||
        CanonicalType, CanonicalTypeKind, CanonicalValue, Connect, DynCanonicalType,
 | 
			
		||||
        DynCanonicalValue, DynType, MatchVariantAndInactiveScope, Type, TypeEnum, Value, ValueEnum,
 | 
			
		||||
    },
 | 
			
		||||
};
 | 
			
		||||
use bitvec::{order::Lsb0, slice::BitSlice, vec::BitVec, view::BitView};
 | 
			
		||||
use hashbrown::HashMap;
 | 
			
		||||
use std::{
 | 
			
		||||
    borrow::Cow,
 | 
			
		||||
    fmt,
 | 
			
		||||
    hash::{Hash, Hasher},
 | 
			
		||||
    iter::FusedIterator,
 | 
			
		||||
    marker::PhantomData,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
 | 
			
		||||
pub struct VariantType<T> {
 | 
			
		||||
    pub name: Interned<str>,
 | 
			
		||||
    pub ty: Option<T>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct FmtDebugInEnum<'a, T>(&'a VariantType<T>);
 | 
			
		||||
 | 
			
		||||
impl<T: fmt::Debug> fmt::Debug for FmtDebugInEnum<'_, T> {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        let VariantType { name, ref ty } = *self.0;
 | 
			
		||||
        if let Some(ty) = ty {
 | 
			
		||||
            write!(f, "{name}({ty:?})")
 | 
			
		||||
        } else {
 | 
			
		||||
            write!(f, "{name}")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: fmt::Debug> fmt::Display for FmtDebugInEnum<'_, T> {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        fmt::Debug::fmt(self, f)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T> VariantType<T> {
 | 
			
		||||
    pub fn map_opt_ty<U, F: FnOnce(Option<T>) -> Option<U>>(self, f: F) -> VariantType<U> {
 | 
			
		||||
        let Self { name, ty } = self;
 | 
			
		||||
        VariantType { name, ty: f(ty) }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn map_ty<U, F: FnOnce(T) -> U>(self, f: F) -> VariantType<U> {
 | 
			
		||||
        let Self { name, ty } = self;
 | 
			
		||||
        VariantType {
 | 
			
		||||
            name,
 | 
			
		||||
            ty: ty.map(f),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn as_ref_ty(&self) -> VariantType<&T> {
 | 
			
		||||
        VariantType {
 | 
			
		||||
            name: self.name,
 | 
			
		||||
            ty: self.ty.as_ref(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn fmt_debug_in_enum(&self) -> FmtDebugInEnum<T> {
 | 
			
		||||
        FmtDebugInEnum(self)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: Type> VariantType<T> {
 | 
			
		||||
    pub fn canonical(&self) -> VariantType<T::CanonicalType> {
 | 
			
		||||
        self.as_ref_ty().map_ty(T::canonical)
 | 
			
		||||
    }
 | 
			
		||||
    pub fn to_dyn(&self) -> VariantType<Interned<dyn DynType>> {
 | 
			
		||||
        self.as_ref_ty().map_ty(T::to_dyn)
 | 
			
		||||
    }
 | 
			
		||||
    pub fn canonical_dyn(&self) -> VariantType<Interned<dyn DynCanonicalType>> {
 | 
			
		||||
        self.as_ref_ty().map_ty(T::canonical_dyn)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl VariantType<Interned<dyn DynCanonicalType>> {
 | 
			
		||||
    pub fn from_canonical_type_helper_has_value<T: Type>(self, expected_name: &str) -> T {
 | 
			
		||||
        assert_eq!(&*self.name, expected_name, "variant name doesn't match");
 | 
			
		||||
        let Some(ty) = self.ty else {
 | 
			
		||||
            panic!("variant {expected_name} has no value but a value is expected");
 | 
			
		||||
        };
 | 
			
		||||
        T::from_dyn_canonical_type(ty)
 | 
			
		||||
    }
 | 
			
		||||
    pub fn from_canonical_type_helper_no_value(self, expected_name: &str) {
 | 
			
		||||
        assert_eq!(&*self.name, expected_name, "variant name doesn't match");
 | 
			
		||||
        assert!(
 | 
			
		||||
            self.ty.is_none(),
 | 
			
		||||
            "variant {expected_name} has a value but is expected to have no value"
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Eq)]
 | 
			
		||||
struct DynEnumTypeImpl {
 | 
			
		||||
    variants: Interned<[VariantType<Interned<dyn DynCanonicalType>>]>,
 | 
			
		||||
    name_indexes: HashMap<Interned<str>, usize>,
 | 
			
		||||
    bit_width: usize,
 | 
			
		||||
    is_storable: bool,
 | 
			
		||||
    is_castable_from_bits: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl fmt::Debug for DynEnumTypeImpl {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        write!(f, "DynEnumType ")?;
 | 
			
		||||
        f.debug_set()
 | 
			
		||||
            .entries(
 | 
			
		||||
                self.variants
 | 
			
		||||
                    .iter()
 | 
			
		||||
                    .map(|variant| variant.fmt_debug_in_enum()),
 | 
			
		||||
            )
 | 
			
		||||
            .finish()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl PartialEq for DynEnumTypeImpl {
 | 
			
		||||
    fn eq(&self, other: &Self) -> bool {
 | 
			
		||||
        self.variants == other.variants
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Hash for DynEnumTypeImpl {
 | 
			
		||||
    fn hash<H: Hasher>(&self, state: &mut H) {
 | 
			
		||||
        self.variants.hash(state);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Copy, Clone, Hash, PartialEq, Eq)]
 | 
			
		||||
pub struct DynEnumType(Interned<DynEnumTypeImpl>);
 | 
			
		||||
 | 
			
		||||
impl fmt::Debug for DynEnumType {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        self.0.fmt(f)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn discriminant_bit_width_impl(variant_count: usize) -> usize {
 | 
			
		||||
    variant_count
 | 
			
		||||
        .next_power_of_two()
 | 
			
		||||
        .checked_ilog2()
 | 
			
		||||
        .unwrap_or(0) as usize
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl DynEnumType {
 | 
			
		||||
    #[track_caller]
 | 
			
		||||
    pub fn new(variants: Interned<[VariantType<Interned<dyn DynCanonicalType>>]>) -> Self {
 | 
			
		||||
        assert!(!variants.is_empty(), "zero-variant enums aren't yet supported: https://github.com/chipsalliance/firrtl-spec/issues/208");
 | 
			
		||||
        let mut name_indexes = HashMap::with_capacity(variants.len());
 | 
			
		||||
        let mut body_bit_width = 0usize;
 | 
			
		||||
        let mut is_storable = true;
 | 
			
		||||
        let mut is_castable_from_bits = true;
 | 
			
		||||
        for (index, &VariantType { name, ty }) in variants.iter().enumerate() {
 | 
			
		||||
            if let Some(old_index) = name_indexes.insert(name, index) {
 | 
			
		||||
                panic!("duplicate variant name {name:?}: at both index {old_index} and {index}");
 | 
			
		||||
            }
 | 
			
		||||
            if let Some(ty) = ty {
 | 
			
		||||
                assert!(
 | 
			
		||||
                    ty.is_passive(),
 | 
			
		||||
                    "variant type must be a passive type: {ty:?}"
 | 
			
		||||
                );
 | 
			
		||||
                body_bit_width = body_bit_width.max(ty.bit_width());
 | 
			
		||||
                is_storable &= ty.is_storable();
 | 
			
		||||
                is_castable_from_bits &= ty.is_castable_from_bits();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        let bit_width = body_bit_width
 | 
			
		||||
            .checked_add(discriminant_bit_width_impl(variants.len()))
 | 
			
		||||
            .unwrap_or_else(|| panic!("enum is too big: bit-width overflowed"));
 | 
			
		||||
        Self(
 | 
			
		||||
            DynEnumTypeImpl {
 | 
			
		||||
                variants,
 | 
			
		||||
                name_indexes,
 | 
			
		||||
                bit_width,
 | 
			
		||||
                is_storable,
 | 
			
		||||
                is_castable_from_bits,
 | 
			
		||||
            }
 | 
			
		||||
            .intern_sized(),
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
    pub fn discriminant_bit_width(self) -> usize {
 | 
			
		||||
        discriminant_bit_width_impl(self.variants().len())
 | 
			
		||||
    }
 | 
			
		||||
    pub fn is_passive(self) -> bool {
 | 
			
		||||
        true
 | 
			
		||||
    }
 | 
			
		||||
    pub fn is_storable(self) -> bool {
 | 
			
		||||
        self.0.is_storable
 | 
			
		||||
    }
 | 
			
		||||
    pub fn is_castable_from_bits(self) -> bool {
 | 
			
		||||
        self.0.is_castable_from_bits
 | 
			
		||||
    }
 | 
			
		||||
    pub fn bit_width(self) -> usize {
 | 
			
		||||
        self.0.bit_width
 | 
			
		||||
    }
 | 
			
		||||
    pub fn name_indexes(&self) -> &HashMap<Interned<str>, usize> {
 | 
			
		||||
        &self.0.name_indexes
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
 | 
			
		||||
pub struct DynEnum {
 | 
			
		||||
    ty: DynEnumType,
 | 
			
		||||
    variant_index: usize,
 | 
			
		||||
    variant_value: Option<DynCanonicalValue>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl DynEnum {
 | 
			
		||||
    #[track_caller]
 | 
			
		||||
    pub fn new_by_index(
 | 
			
		||||
        ty: DynEnumType,
 | 
			
		||||
        variant_index: usize,
 | 
			
		||||
        variant_value: Option<DynCanonicalValue>,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        let variant = ty.variants()[variant_index];
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            variant_value.as_ref().map(|v| v.ty()),
 | 
			
		||||
            variant.ty,
 | 
			
		||||
            "variant value doesn't match type"
 | 
			
		||||
        );
 | 
			
		||||
        Self {
 | 
			
		||||
            ty,
 | 
			
		||||
            variant_index,
 | 
			
		||||
            variant_value,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    #[track_caller]
 | 
			
		||||
    pub fn new_by_name(
 | 
			
		||||
        ty: DynEnumType,
 | 
			
		||||
        variant_name: Interned<str>,
 | 
			
		||||
        variant_value: Option<DynCanonicalValue>,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        let variant_index = ty.name_indexes()[&variant_name];
 | 
			
		||||
        Self::new_by_index(ty, variant_index, variant_value)
 | 
			
		||||
    }
 | 
			
		||||
    pub fn variant_index(&self) -> usize {
 | 
			
		||||
        self.variant_index
 | 
			
		||||
    }
 | 
			
		||||
    pub fn variant_value(&self) -> &Option<DynCanonicalValue> {
 | 
			
		||||
        &self.variant_value
 | 
			
		||||
    }
 | 
			
		||||
    pub fn variant_with_type(&self) -> VariantType<Interned<dyn DynCanonicalType>> {
 | 
			
		||||
        self.ty.variants()[self.variant_index]
 | 
			
		||||
    }
 | 
			
		||||
    pub fn variant_name(&self) -> Interned<str> {
 | 
			
		||||
        self.variant_with_type().name
 | 
			
		||||
    }
 | 
			
		||||
    pub fn variant_type(&self) -> Option<Interned<dyn DynCanonicalType>> {
 | 
			
		||||
        self.variant_with_type().ty
 | 
			
		||||
    }
 | 
			
		||||
    pub fn variant_with_value(&self) -> VariantType<&DynCanonicalValue> {
 | 
			
		||||
        self.variant_with_type()
 | 
			
		||||
            .map_opt_ty(|_| self.variant_value.as_ref())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
 | 
			
		||||
pub struct VariantsHint {
 | 
			
		||||
    pub known_variants: Interned<[VariantType<Interned<dyn TypeHintTrait>>]>,
 | 
			
		||||
    pub more_variants: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl VariantsHint {
 | 
			
		||||
    pub fn new(
 | 
			
		||||
        known_variants: impl IntoIterator<Item = VariantType<Interned<dyn TypeHintTrait>>>,
 | 
			
		||||
        more_variants: bool,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        let known_variants = Intern::intern_owned(Vec::from_iter(known_variants));
 | 
			
		||||
        Self {
 | 
			
		||||
            known_variants,
 | 
			
		||||
            more_variants,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn check_variant(
 | 
			
		||||
        self,
 | 
			
		||||
        index: usize,
 | 
			
		||||
        variant: VariantType<&dyn DynType>,
 | 
			
		||||
    ) -> Result<(), String> {
 | 
			
		||||
        let Some(&known_variant) = self.known_variants.get(index) else {
 | 
			
		||||
            return if self.more_variants {
 | 
			
		||||
                Ok(())
 | 
			
		||||
            } else {
 | 
			
		||||
                Err(format!(
 | 
			
		||||
                    "too many variants: name={:?} index={index}",
 | 
			
		||||
                    variant.name
 | 
			
		||||
                ))
 | 
			
		||||
            };
 | 
			
		||||
        };
 | 
			
		||||
        let VariantType {
 | 
			
		||||
            name: known_name,
 | 
			
		||||
            ty: type_hint,
 | 
			
		||||
        } = known_variant;
 | 
			
		||||
        let VariantType { name, ty } = variant;
 | 
			
		||||
        if name != known_name {
 | 
			
		||||
            Err(format!(
 | 
			
		||||
                "wrong variant name {name:?}, expected {known_name:?}"
 | 
			
		||||
            ))
 | 
			
		||||
        } else {
 | 
			
		||||
            match (ty, type_hint) {
 | 
			
		||||
                (Some(ty), Some(type_hint)) => type_hint.matches(ty),
 | 
			
		||||
                (None, None) => Ok(()),
 | 
			
		||||
                (None, Some(_)) => Err(format!(
 | 
			
		||||
                    "expected variant {name:?} to have type, no type provided"
 | 
			
		||||
                )),
 | 
			
		||||
                (Some(_), None) => Err(format!(
 | 
			
		||||
                    "expected variant {name:?} to have no type, but a type was provided"
 | 
			
		||||
                )),
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub trait EnumType:
 | 
			
		||||
    Type<
 | 
			
		||||
        CanonicalType = DynEnumType,
 | 
			
		||||
        CanonicalValue = DynEnum,
 | 
			
		||||
        MaskType = UIntType<1>,
 | 
			
		||||
        MaskValue = UInt<1>,
 | 
			
		||||
        MatchActiveScope = Scope,
 | 
			
		||||
        MatchVariantAndInactiveScope = EnumMatchVariantAndInactiveScope<Self>,
 | 
			
		||||
        MatchVariantsIter = EnumMatchVariantsIter<Self>,
 | 
			
		||||
    > + Connect<Self>
 | 
			
		||||
where
 | 
			
		||||
    Self::Value: EnumValue + ToExpr<Type = Self>,
 | 
			
		||||
{
 | 
			
		||||
    type Builder;
 | 
			
		||||
    fn match_activate_scope(
 | 
			
		||||
        v: Self::MatchVariantAndInactiveScope,
 | 
			
		||||
    ) -> (Self::MatchVariant, Self::MatchActiveScope);
 | 
			
		||||
    fn builder() -> Self::Builder;
 | 
			
		||||
    fn variants(&self) -> Interned<[VariantType<Interned<dyn DynCanonicalType>>]>;
 | 
			
		||||
    fn variants_hint() -> VariantsHint;
 | 
			
		||||
    #[allow(clippy::result_unit_err)]
 | 
			
		||||
    fn variant_to_bits<VariantValue: ToExpr + Eq + Hash + Send + Sync + 'static + Clone>(
 | 
			
		||||
        &self,
 | 
			
		||||
        variant_index: usize,
 | 
			
		||||
        variant_value: Option<&VariantValue>,
 | 
			
		||||
    ) -> Result<Interned<BitSlice>, ()> {
 | 
			
		||||
        #[derive(Hash, Eq, PartialEq)]
 | 
			
		||||
        struct VariantToBitsMemoize<E, V>(PhantomData<(E, V)>);
 | 
			
		||||
        impl<E, V> Clone for VariantToBitsMemoize<E, V> {
 | 
			
		||||
            fn clone(&self) -> Self {
 | 
			
		||||
                *self
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        impl<E, V> Copy for VariantToBitsMemoize<E, V> {}
 | 
			
		||||
        impl<
 | 
			
		||||
                E: EnumType<Value: EnumValue<Type = E>>,
 | 
			
		||||
                V: ToExpr + Eq + Hash + Send + Sync + 'static + Clone,
 | 
			
		||||
            > MemoizeGeneric for VariantToBitsMemoize<E, V>
 | 
			
		||||
        {
 | 
			
		||||
            type InputRef<'a> = (&'a E, usize, Option<&'a V>);
 | 
			
		||||
            type InputOwned = (E, usize, Option<V>);
 | 
			
		||||
            type InputCow<'a> = (Cow<'a, E>, usize, Option<Cow<'a, V>>);
 | 
			
		||||
            type Output = Result<Interned<BitSlice>, ()>;
 | 
			
		||||
 | 
			
		||||
            fn input_borrow(input: &Self::InputOwned) -> Self::InputRef<'_> {
 | 
			
		||||
                (&input.0, input.1, input.2.as_ref())
 | 
			
		||||
            }
 | 
			
		||||
            fn input_eq(a: Self::InputRef<'_>, b: Self::InputRef<'_>) -> bool {
 | 
			
		||||
                a == b
 | 
			
		||||
            }
 | 
			
		||||
            fn input_cow_into_owned(input: Self::InputCow<'_>) -> Self::InputOwned {
 | 
			
		||||
                (input.0.into_owned(), input.1, input.2.map(Cow::into_owned))
 | 
			
		||||
            }
 | 
			
		||||
            fn input_cow_borrow<'a>(input: &'a Self::InputCow<'_>) -> Self::InputRef<'a> {
 | 
			
		||||
                (&input.0, input.1, input.2.as_deref())
 | 
			
		||||
            }
 | 
			
		||||
            fn input_cow_from_owned<'a>(input: Self::InputOwned) -> Self::InputCow<'a> {
 | 
			
		||||
                (Cow::Owned(input.0), input.1, input.2.map(Cow::Owned))
 | 
			
		||||
            }
 | 
			
		||||
            fn input_cow_from_ref(input: Self::InputRef<'_>) -> Self::InputCow<'_> {
 | 
			
		||||
                (Cow::Borrowed(input.0), input.1, input.2.map(Cow::Borrowed))
 | 
			
		||||
            }
 | 
			
		||||
            fn inner(self, input: Self::InputRef<'_>) -> Self::Output {
 | 
			
		||||
                let (ty, variant_index, variant_value) = input;
 | 
			
		||||
                let ty = ty.canonical();
 | 
			
		||||
                let mut bits = BitVec::with_capacity(ty.bit_width());
 | 
			
		||||
                bits.extend_from_bitslice(
 | 
			
		||||
                    &variant_index.view_bits::<Lsb0>()[..ty.discriminant_bit_width()],
 | 
			
		||||
                );
 | 
			
		||||
                if let Some(variant_value) = variant_value {
 | 
			
		||||
                    bits.extend_from_bitslice(&variant_value.to_expr().to_literal_bits()?);
 | 
			
		||||
                }
 | 
			
		||||
                bits.resize(ty.bit_width(), false);
 | 
			
		||||
                Ok(Intern::intern_owned(bits))
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        VariantToBitsMemoize::<Self, VariantValue>(PhantomData).get((
 | 
			
		||||
            self,
 | 
			
		||||
            variant_index,
 | 
			
		||||
            variant_value,
 | 
			
		||||
        ))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub trait EnumValue: Value
 | 
			
		||||
where
 | 
			
		||||
    <Self as ToExpr>::Type: EnumType<Value = Self>,
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct EnumMatchVariantAndInactiveScope<T: EnumType>(EnumMatchVariantAndInactiveScopeImpl<T>)
 | 
			
		||||
where
 | 
			
		||||
    T::Value: EnumValue<Type = T>;
 | 
			
		||||
 | 
			
		||||
impl<T: EnumType> MatchVariantAndInactiveScope for EnumMatchVariantAndInactiveScope<T>
 | 
			
		||||
where
 | 
			
		||||
    T::Value: EnumValue<Type = T>,
 | 
			
		||||
{
 | 
			
		||||
    type MatchVariant = T::MatchVariant;
 | 
			
		||||
    type MatchActiveScope = Scope;
 | 
			
		||||
 | 
			
		||||
    fn match_activate_scope(self) -> (Self::MatchVariant, Self::MatchActiveScope) {
 | 
			
		||||
        T::match_activate_scope(self)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: EnumType> EnumMatchVariantAndInactiveScope<T>
 | 
			
		||||
where
 | 
			
		||||
    T::Value: EnumValue<Type = T>,
 | 
			
		||||
{
 | 
			
		||||
    pub fn variant_access(&self) -> Interned<VariantAccess<T, Interned<dyn DynCanonicalType>>> {
 | 
			
		||||
        self.0.variant_access()
 | 
			
		||||
    }
 | 
			
		||||
    pub fn activate(
 | 
			
		||||
        self,
 | 
			
		||||
    ) -> (
 | 
			
		||||
        Interned<VariantAccess<T, Interned<dyn DynCanonicalType>>>,
 | 
			
		||||
        Scope,
 | 
			
		||||
    ) {
 | 
			
		||||
        self.0.activate()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone)]
 | 
			
		||||
pub struct EnumMatchVariantsIter<T: EnumType>
 | 
			
		||||
where
 | 
			
		||||
    T::Value: EnumValue<Type = T>,
 | 
			
		||||
{
 | 
			
		||||
    pub(crate) inner: EnumMatchVariantsIterImpl<T>,
 | 
			
		||||
    pub(crate) variant_index: std::ops::Range<usize>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: EnumType> Iterator for EnumMatchVariantsIter<T>
 | 
			
		||||
where
 | 
			
		||||
    T::Value: EnumValue<Type = T>,
 | 
			
		||||
{
 | 
			
		||||
    type Item = EnumMatchVariantAndInactiveScope<T>;
 | 
			
		||||
 | 
			
		||||
    fn next(&mut self) -> Option<Self::Item> {
 | 
			
		||||
        self.variant_index.next().map(|variant_index| {
 | 
			
		||||
            EnumMatchVariantAndInactiveScope(self.inner.for_variant_index(variant_index))
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn size_hint(&self) -> (usize, Option<usize>) {
 | 
			
		||||
        self.variant_index.size_hint()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: EnumType> ExactSizeIterator for EnumMatchVariantsIter<T>
 | 
			
		||||
where
 | 
			
		||||
    T::Value: EnumValue<Type = T>,
 | 
			
		||||
{
 | 
			
		||||
    fn len(&self) -> usize {
 | 
			
		||||
        self.variant_index.len()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: EnumType> FusedIterator for EnumMatchVariantsIter<T> where T::Value: EnumValue<Type = T> {}
 | 
			
		||||
 | 
			
		||||
impl<T: EnumType> DoubleEndedIterator for EnumMatchVariantsIter<T>
 | 
			
		||||
where
 | 
			
		||||
    T::Value: EnumValue<Type = T>,
 | 
			
		||||
{
 | 
			
		||||
    fn next_back(&mut self) -> Option<Self::Item> {
 | 
			
		||||
        self.variant_index.next_back().map(|variant_index| {
 | 
			
		||||
            EnumMatchVariantAndInactiveScope(self.inner.for_variant_index(variant_index))
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Type for DynEnumType {
 | 
			
		||||
    type CanonicalType = DynEnumType;
 | 
			
		||||
    type Value = DynEnum;
 | 
			
		||||
    type CanonicalValue = DynEnum;
 | 
			
		||||
    type MaskType = UIntType<1>;
 | 
			
		||||
    type MaskValue = UInt<1>;
 | 
			
		||||
    type MatchVariant = Option<Expr<DynCanonicalValue>>;
 | 
			
		||||
    type MatchActiveScope = Scope;
 | 
			
		||||
    type MatchVariantAndInactiveScope = EnumMatchVariantAndInactiveScope<Self>;
 | 
			
		||||
    type MatchVariantsIter = EnumMatchVariantsIter<Self>;
 | 
			
		||||
 | 
			
		||||
    fn match_variants<IO: BundleValue>(
 | 
			
		||||
        this: Expr<Self::Value>,
 | 
			
		||||
        module_builder: &mut ModuleBuilder<IO, NormalModule>,
 | 
			
		||||
        source_location: SourceLocation,
 | 
			
		||||
    ) -> Self::MatchVariantsIter
 | 
			
		||||
    where
 | 
			
		||||
        IO::Type: crate::bundle::BundleType<Value = IO>,
 | 
			
		||||
    {
 | 
			
		||||
        module_builder.enum_match_variants_helper(this, source_location)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn mask_type(&self) -> Self::MaskType {
 | 
			
		||||
        UIntType::new()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn canonical(&self) -> Self::CanonicalType {
 | 
			
		||||
        *self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn source_location(&self) -> SourceLocation {
 | 
			
		||||
        SourceLocation::builtin()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn type_enum(&self) -> TypeEnum {
 | 
			
		||||
        TypeEnum::EnumType(*self)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn from_canonical_type(t: Self::CanonicalType) -> Self {
 | 
			
		||||
        t
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> {
 | 
			
		||||
        Some(this)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Connect<Self> for DynEnumType {}
 | 
			
		||||
 | 
			
		||||
pub struct NoBuilder;
 | 
			
		||||
 | 
			
		||||
impl EnumType for DynEnumType {
 | 
			
		||||
    type Builder = NoBuilder;
 | 
			
		||||
 | 
			
		||||
    fn match_activate_scope(
 | 
			
		||||
        v: Self::MatchVariantAndInactiveScope,
 | 
			
		||||
    ) -> (Self::MatchVariant, Self::MatchActiveScope) {
 | 
			
		||||
        let (expr, scope) = v.0.activate();
 | 
			
		||||
        (expr.variant_type().ty.map(|_| expr.to_expr()), scope)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn builder() -> Self::Builder {
 | 
			
		||||
        NoBuilder
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn variants(&self) -> Interned<[VariantType<Interned<dyn DynCanonicalType>>]> {
 | 
			
		||||
        self.0.variants
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn variants_hint() -> VariantsHint {
 | 
			
		||||
        VariantsHint {
 | 
			
		||||
            known_variants: [][..].intern(),
 | 
			
		||||
            more_variants: true,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl CanonicalType for DynEnumType {
 | 
			
		||||
    const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::EnumType;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ToExpr for DynEnum {
 | 
			
		||||
    type Type = DynEnumType;
 | 
			
		||||
 | 
			
		||||
    fn ty(&self) -> Self::Type {
 | 
			
		||||
        self.ty
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
 | 
			
		||||
        Expr::from_value(self)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Value for DynEnum {
 | 
			
		||||
    fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue {
 | 
			
		||||
        self.clone()
 | 
			
		||||
    }
 | 
			
		||||
    fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
 | 
			
		||||
        this.ty
 | 
			
		||||
            .variant_to_bits(this.variant_index, this.variant_value.as_ref())
 | 
			
		||||
            .unwrap()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl EnumValue for DynEnum {}
 | 
			
		||||
 | 
			
		||||
impl CanonicalValue for DynEnum {
 | 
			
		||||
    fn value_enum_impl(this: &Self) -> ValueEnum {
 | 
			
		||||
        ValueEnum::Enum(this.clone())
 | 
			
		||||
    }
 | 
			
		||||
    fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
 | 
			
		||||
        this.ty
 | 
			
		||||
            .variant_to_bits(this.variant_index, this.variant_value.as_ref())
 | 
			
		||||
            .unwrap()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
mod impl_option {
 | 
			
		||||
    #[allow(dead_code)]
 | 
			
		||||
    #[derive(crate::Value)]
 | 
			
		||||
    #[hdl(target(std::option::Option), connect_inexact, outline_generated)]
 | 
			
		||||
    pub enum Option<T> {
 | 
			
		||||
        None,
 | 
			
		||||
        Some(T),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1090
									
								
								crates/fayalite/src/expr.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1090
									
								
								crates/fayalite/src/expr.rs
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										1590
									
								
								crates/fayalite/src/expr/ops.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1590
									
								
								crates/fayalite/src/expr/ops.rs
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										2470
									
								
								crates/fayalite/src/firrtl.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2470
									
								
								crates/fayalite/src/firrtl.rs
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										1176
									
								
								crates/fayalite/src/int.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1176
									
								
								crates/fayalite/src/int.rs
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										1076
									
								
								crates/fayalite/src/intern.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1076
									
								
								crates/fayalite/src/intern.rs
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										138
									
								
								crates/fayalite/src/intern/type_map.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								crates/fayalite/src/intern/type_map.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,138 @@
 | 
			
		|||
// SPDX-License-Identifier: LGPL-3.0-or-later
 | 
			
		||||
// See Notices.txt for copyright information
 | 
			
		||||
use hashbrown::HashMap;
 | 
			
		||||
use std::{
 | 
			
		||||
    any::{Any, TypeId},
 | 
			
		||||
    hash::{BuildHasher, Hasher},
 | 
			
		||||
    ptr::NonNull,
 | 
			
		||||
    sync::RwLock,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct TypeIdHasher(u64);
 | 
			
		||||
 | 
			
		||||
// assumes TypeId has at least 64 bits that is a good hash
 | 
			
		||||
impl Hasher for TypeIdHasher {
 | 
			
		||||
    #[inline(always)]
 | 
			
		||||
    fn finish(&self) -> u64 {
 | 
			
		||||
        self.0
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[inline(always)]
 | 
			
		||||
    fn write(&mut self, bytes: &[u8]) {
 | 
			
		||||
        for &byte in bytes {
 | 
			
		||||
            self.write_u8(byte);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[inline(always)]
 | 
			
		||||
    fn write_u8(&mut self, i: u8) {
 | 
			
		||||
        self.0 = self.0.rotate_left(8);
 | 
			
		||||
        self.0 ^= i as u64;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[inline(always)]
 | 
			
		||||
    fn write_u16(&mut self, i: u16) {
 | 
			
		||||
        self.0 = self.0.rotate_left(16);
 | 
			
		||||
        self.0 ^= i as u64;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[inline(always)]
 | 
			
		||||
    fn write_u32(&mut self, i: u32) {
 | 
			
		||||
        self.0 = self.0.rotate_left(32);
 | 
			
		||||
        self.0 ^= i as u64;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[inline(always)]
 | 
			
		||||
    fn write_u64(&mut self, i: u64) {
 | 
			
		||||
        self.0 ^= i;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[inline(always)]
 | 
			
		||||
    fn write_u128(&mut self, i: u128) {
 | 
			
		||||
        self.write_u64(i as u64);
 | 
			
		||||
        self.write_u64((i >> 64) as u64);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[inline(always)]
 | 
			
		||||
    fn write_usize(&mut self, i: usize) {
 | 
			
		||||
        match usize::BITS {
 | 
			
		||||
            128 => self.write_u128(i as u128),
 | 
			
		||||
            64 => self.write_u64(i as u64),
 | 
			
		||||
            32 => self.write_u32(i as u32),
 | 
			
		||||
            16 => self.write_u16(i as u16),
 | 
			
		||||
            _ => self.write(&i.to_ne_bytes()),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct TypeIdBuildHasher;
 | 
			
		||||
 | 
			
		||||
impl BuildHasher for TypeIdBuildHasher {
 | 
			
		||||
    type Hasher = TypeIdHasher;
 | 
			
		||||
 | 
			
		||||
    fn build_hasher(&self) -> Self::Hasher {
 | 
			
		||||
        TypeIdHasher(0)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct Value(NonNull<dyn Any + Send + Sync>);
 | 
			
		||||
 | 
			
		||||
impl Value {
 | 
			
		||||
    unsafe fn get_transmute_lifetime<'b>(&self) -> &'b (dyn Any + Send + Sync) {
 | 
			
		||||
        unsafe { &*self.0.as_ptr() }
 | 
			
		||||
    }
 | 
			
		||||
    fn new(v: Box<dyn Any + Send + Sync>) -> Self {
 | 
			
		||||
        unsafe { Self(NonNull::new_unchecked(Box::into_raw(v))) }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsafe impl Send for Value {}
 | 
			
		||||
unsafe impl Sync for Value {}
 | 
			
		||||
 | 
			
		||||
impl Drop for Value {
 | 
			
		||||
    fn drop(&mut self) {
 | 
			
		||||
        unsafe { std::ptr::drop_in_place(self.0.as_ptr()) }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct TypeIdMap(RwLock<HashMap<TypeId, Value, TypeIdBuildHasher>>);
 | 
			
		||||
 | 
			
		||||
impl TypeIdMap {
 | 
			
		||||
    pub const fn new() -> Self {
 | 
			
		||||
        Self(RwLock::new(HashMap::with_hasher(TypeIdBuildHasher)))
 | 
			
		||||
    }
 | 
			
		||||
    #[cold]
 | 
			
		||||
    unsafe fn insert_slow(
 | 
			
		||||
        &self,
 | 
			
		||||
        type_id: TypeId,
 | 
			
		||||
        make: fn() -> Box<dyn Any + Sync + Send>,
 | 
			
		||||
    ) -> &(dyn Any + Sync + Send) {
 | 
			
		||||
        let value = Value::new(make());
 | 
			
		||||
        let mut write_guard = self.0.write().unwrap();
 | 
			
		||||
        unsafe {
 | 
			
		||||
            write_guard
 | 
			
		||||
                .entry(type_id)
 | 
			
		||||
                .or_insert(value)
 | 
			
		||||
                .get_transmute_lifetime()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn get_or_insert_default<T: Sized + Any + Send + Sync + Default>(&self) -> &T {
 | 
			
		||||
        let type_id = TypeId::of::<T>();
 | 
			
		||||
        let read_guard = self.0.read().unwrap();
 | 
			
		||||
        let retval = read_guard
 | 
			
		||||
            .get(&type_id)
 | 
			
		||||
            .map(|v| unsafe { Value::get_transmute_lifetime(v) });
 | 
			
		||||
        drop(read_guard);
 | 
			
		||||
        let retval = match retval {
 | 
			
		||||
            Some(retval) => retval,
 | 
			
		||||
            None => unsafe { self.insert_slow(type_id, move || Box::new(T::default())) },
 | 
			
		||||
        };
 | 
			
		||||
        unsafe { &*(retval as *const dyn Any as *const T) }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for TypeIdMap {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        Self::new()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										27
									
								
								crates/fayalite/src/lib.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								crates/fayalite/src/lib.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,27 @@
 | 
			
		|||
// SPDX-License-Identifier: LGPL-3.0-or-later
 | 
			
		||||
// See Notices.txt for copyright information
 | 
			
		||||
extern crate self as fayalite;
 | 
			
		||||
 | 
			
		||||
#[doc(hidden)]
 | 
			
		||||
pub use std as __std;
 | 
			
		||||
 | 
			
		||||
pub use fayalite_proc_macros::{hdl_module, Value};
 | 
			
		||||
 | 
			
		||||
pub mod annotations;
 | 
			
		||||
pub mod array;
 | 
			
		||||
pub mod bundle;
 | 
			
		||||
pub mod clock;
 | 
			
		||||
pub mod enum_;
 | 
			
		||||
pub mod expr;
 | 
			
		||||
pub mod firrtl;
 | 
			
		||||
pub mod int;
 | 
			
		||||
pub mod intern;
 | 
			
		||||
pub mod memory;
 | 
			
		||||
pub mod module;
 | 
			
		||||
pub mod reg;
 | 
			
		||||
pub mod reset;
 | 
			
		||||
pub mod source_location;
 | 
			
		||||
pub mod ty;
 | 
			
		||||
pub mod util;
 | 
			
		||||
pub mod valueless;
 | 
			
		||||
pub mod wire;
 | 
			
		||||
							
								
								
									
										1094
									
								
								crates/fayalite/src/memory.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1094
									
								
								crates/fayalite/src/memory.rs
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										2615
									
								
								crates/fayalite/src/module.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2615
									
								
								crates/fayalite/src/module.rs
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										5
									
								
								crates/fayalite/src/module/transform.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								crates/fayalite/src/module/transform.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,5 @@
 | 
			
		|||
// SPDX-License-Identifier: LGPL-3.0-or-later
 | 
			
		||||
// See Notices.txt for copyright information
 | 
			
		||||
pub mod simplify_enums;
 | 
			
		||||
pub mod simplify_memories;
 | 
			
		||||
pub mod visit;
 | 
			
		||||
							
								
								
									
										654
									
								
								crates/fayalite/src/module/transform/simplify_enums.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										654
									
								
								crates/fayalite/src/module/transform/simplify_enums.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,654 @@
 | 
			
		|||
// SPDX-License-Identifier: LGPL-3.0-or-later
 | 
			
		||||
// See Notices.txt for copyright information
 | 
			
		||||
use crate::{
 | 
			
		||||
    array::{Array, ArrayType},
 | 
			
		||||
    bundle::{BundleType, BundleValue, DynBundle, DynBundleType},
 | 
			
		||||
    enum_::{DynEnum, DynEnumType, EnumType, EnumValue, VariantType},
 | 
			
		||||
    expr::{ops, Expr, ExprEnum, ToExpr},
 | 
			
		||||
    int::{DynUInt, DynUIntType, IntCmp},
 | 
			
		||||
    intern::{Intern, Interned},
 | 
			
		||||
    memory::{DynPortType, Mem, MemPort},
 | 
			
		||||
    module::{
 | 
			
		||||
        transform::visit::{Fold, Folder},
 | 
			
		||||
        Block, Module, NameIdGen, ScopedNameId, Stmt, StmtConnect, StmtIf, StmtMatch, StmtWire,
 | 
			
		||||
    },
 | 
			
		||||
    source_location::SourceLocation,
 | 
			
		||||
    ty::{DynCanonicalType, DynCanonicalValue, Type, TypeEnum, Value, ValueEnum},
 | 
			
		||||
    wire::Wire,
 | 
			
		||||
};
 | 
			
		||||
use core::fmt;
 | 
			
		||||
use hashbrown::HashMap;
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub enum SimplifyEnumsError {
 | 
			
		||||
    EnumIsNotCastableFromBits { enum_type: DynEnumType },
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl fmt::Display for SimplifyEnumsError {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        match self {
 | 
			
		||||
            SimplifyEnumsError::EnumIsNotCastableFromBits { enum_type } => write!(
 | 
			
		||||
                f,
 | 
			
		||||
                "simplify_enums failed: enum type is not castable from bits: {enum_type:?}"
 | 
			
		||||
            ),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl std::error::Error for SimplifyEnumsError {}
 | 
			
		||||
 | 
			
		||||
#[derive(Value, Clone, Eq, PartialEq, Hash, Debug)]
 | 
			
		||||
struct TagAndBody<T> {
 | 
			
		||||
    tag: T,
 | 
			
		||||
    body: DynUInt,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type TagAndBodyType<T> = <TagAndBody<T> as ToExpr>::Type;
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug)]
 | 
			
		||||
enum EnumTypeState {
 | 
			
		||||
    TagEnumAndBody(TagAndBodyType<DynEnum>),
 | 
			
		||||
    TagUIntAndBody(TagAndBodyType<DynUInt>),
 | 
			
		||||
    UInt(DynUIntType),
 | 
			
		||||
    Unchanged,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct State {
 | 
			
		||||
    enum_types: HashMap<DynEnumType, EnumTypeState>,
 | 
			
		||||
    replacement_mem_ports:
 | 
			
		||||
        HashMap<Interned<MemPort<DynPortType>>, Interned<Wire<Interned<dyn DynCanonicalType>>>>,
 | 
			
		||||
    kind: SimplifyEnumsKind,
 | 
			
		||||
    name_id_gen: NameIdGen,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl State {
 | 
			
		||||
    fn get_or_make_enum_type_state(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        enum_type: DynEnumType,
 | 
			
		||||
    ) -> Result<EnumTypeState, SimplifyEnumsError> {
 | 
			
		||||
        if let Some(retval) = self.enum_types.get(&enum_type) {
 | 
			
		||||
            return Ok(retval.clone());
 | 
			
		||||
        }
 | 
			
		||||
        if !enum_type.is_castable_from_bits() {
 | 
			
		||||
            return Err(SimplifyEnumsError::EnumIsNotCastableFromBits { enum_type });
 | 
			
		||||
        }
 | 
			
		||||
        let has_body = enum_type
 | 
			
		||||
            .variants()
 | 
			
		||||
            .iter()
 | 
			
		||||
            .any(|variant| variant.ty.is_some());
 | 
			
		||||
        let retval = match (self.kind, has_body) {
 | 
			
		||||
            (SimplifyEnumsKind::SimplifyToEnumsWithNoBody, true) => {
 | 
			
		||||
                EnumTypeState::TagEnumAndBody(TagAndBodyType::<DynEnum> {
 | 
			
		||||
                    tag: DynEnumType::new(Interned::from_iter(enum_type.variants().iter().map(
 | 
			
		||||
                        |v| VariantType {
 | 
			
		||||
                            name: v.name,
 | 
			
		||||
                            ty: None,
 | 
			
		||||
                        },
 | 
			
		||||
                    ))),
 | 
			
		||||
                    body: DynUIntType::new(
 | 
			
		||||
                        enum_type.bit_width() - enum_type.discriminant_bit_width(),
 | 
			
		||||
                    ),
 | 
			
		||||
                })
 | 
			
		||||
            }
 | 
			
		||||
            (SimplifyEnumsKind::SimplifyToEnumsWithNoBody, false) => EnumTypeState::Unchanged,
 | 
			
		||||
            (SimplifyEnumsKind::ReplaceWithBundleOfUInts, _) => {
 | 
			
		||||
                EnumTypeState::TagUIntAndBody(TagAndBodyType::<DynUInt> {
 | 
			
		||||
                    tag: DynUIntType::new(enum_type.discriminant_bit_width()),
 | 
			
		||||
                    body: DynUIntType::new(
 | 
			
		||||
                        enum_type.bit_width() - enum_type.discriminant_bit_width(),
 | 
			
		||||
                    ),
 | 
			
		||||
                })
 | 
			
		||||
            }
 | 
			
		||||
            (SimplifyEnumsKind::ReplaceWithUInt, _) => {
 | 
			
		||||
                EnumTypeState::UInt(DynUIntType::new(enum_type.bit_width()))
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
        self.enum_types.insert(enum_type, retval.clone());
 | 
			
		||||
        Ok(retval)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn value_to_uint<T: Value>(value: Option<&T>, target_ty: DynUIntType) -> DynUInt {
 | 
			
		||||
    let Some(value) = value else {
 | 
			
		||||
        return DynUInt::with_type(target_ty, 0u8);
 | 
			
		||||
    };
 | 
			
		||||
    DynUInt::from_bit_slice(&value.to_bits())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn connect_port(
 | 
			
		||||
    stmts: &mut Vec<Stmt>,
 | 
			
		||||
    lhs: Expr<DynCanonicalValue>,
 | 
			
		||||
    rhs: Expr<DynCanonicalValue>,
 | 
			
		||||
    source_location: SourceLocation,
 | 
			
		||||
) {
 | 
			
		||||
    println!("connect_port: lhs={lhs:?} rhs={rhs:?}");
 | 
			
		||||
    if lhs.canonical_type() == rhs.canonical_type() {
 | 
			
		||||
        stmts.push(
 | 
			
		||||
            dbg!(StmtConnect {
 | 
			
		||||
                lhs,
 | 
			
		||||
                rhs,
 | 
			
		||||
                source_location,
 | 
			
		||||
            })
 | 
			
		||||
            .into(),
 | 
			
		||||
        );
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    match (
 | 
			
		||||
        lhs.canonical_type().type_enum(),
 | 
			
		||||
        rhs.canonical_type().type_enum(),
 | 
			
		||||
    ) {
 | 
			
		||||
        (TypeEnum::BundleType(lhs_type), TypeEnum::UInt(_)) => {
 | 
			
		||||
            let lhs = lhs.with_type::<DynBundle>();
 | 
			
		||||
            for field in lhs_type.fields() {
 | 
			
		||||
                assert!(!field.flipped);
 | 
			
		||||
                connect_port(stmts, lhs.field(&field.name), rhs, source_location);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        (TypeEnum::UInt(_), TypeEnum::BundleType(rhs_type)) => {
 | 
			
		||||
            let rhs = rhs.with_type::<DynBundle>();
 | 
			
		||||
            for field in rhs_type.fields() {
 | 
			
		||||
                assert!(!field.flipped);
 | 
			
		||||
                connect_port(stmts, lhs, rhs.field(&field.name), source_location);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        (TypeEnum::BundleType(lhs_type), TypeEnum::BundleType(_)) => {
 | 
			
		||||
            let lhs = lhs.with_type::<DynBundle>();
 | 
			
		||||
            let rhs = rhs.with_type::<DynBundle>();
 | 
			
		||||
            for field in lhs_type.fields() {
 | 
			
		||||
                let (lhs_field, rhs_field) = if field.flipped {
 | 
			
		||||
                    (rhs.field(&field.name), lhs.field(&field.name))
 | 
			
		||||
                } else {
 | 
			
		||||
                    (lhs.field(&field.name), rhs.field(&field.name))
 | 
			
		||||
                };
 | 
			
		||||
                connect_port(stmts, lhs_field, rhs_field, source_location);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        (TypeEnum::ArrayType(lhs_type), TypeEnum::ArrayType(_)) => {
 | 
			
		||||
            let lhs = lhs.with_type::<Array<[DynCanonicalValue]>>();
 | 
			
		||||
            let rhs = rhs.with_type::<Array<[DynCanonicalValue]>>();
 | 
			
		||||
            for index in 0..lhs_type.len() {
 | 
			
		||||
                connect_port(stmts, lhs[index], rhs[index], source_location);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        (TypeEnum::BundleType(_), _)
 | 
			
		||||
        | (TypeEnum::EnumType(_), _)
 | 
			
		||||
        | (TypeEnum::ArrayType(_), _)
 | 
			
		||||
        | (TypeEnum::UInt(_), _)
 | 
			
		||||
        | (TypeEnum::SInt(_), _)
 | 
			
		||||
        | (TypeEnum::Clock(_), _)
 | 
			
		||||
        | (TypeEnum::AsyncReset(_), _)
 | 
			
		||||
        | (TypeEnum::SyncReset(_), _)
 | 
			
		||||
        | (TypeEnum::Reset(_), _) => unreachable!(
 | 
			
		||||
            "trying to connect memory ports:\n{:?}\n{:?}",
 | 
			
		||||
            lhs.canonical_type().type_enum(),
 | 
			
		||||
            rhs.canonical_type().type_enum(),
 | 
			
		||||
        ),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Folder for State {
 | 
			
		||||
    type Error = SimplifyEnumsError;
 | 
			
		||||
 | 
			
		||||
    fn fold_dyn_enum(&mut self, _v: DynEnum) -> Result<DynEnum, Self::Error> {
 | 
			
		||||
        unreachable!()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn fold_dyn_enum_type(&mut self, _v: DynEnumType) -> Result<DynEnumType, Self::Error> {
 | 
			
		||||
        unreachable!()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn fold_module<T: BundleValue>(&mut self, v: Module<T>) -> Result<Module<T>, Self::Error>
 | 
			
		||||
    where
 | 
			
		||||
        T::Type: BundleType<Value = T>,
 | 
			
		||||
    {
 | 
			
		||||
        let old_name_id_gen =
 | 
			
		||||
            std::mem::replace(&mut self.name_id_gen, NameIdGen::for_module(v.canonical()));
 | 
			
		||||
        let retval = Fold::default_fold(v, self);
 | 
			
		||||
        self.name_id_gen = old_name_id_gen;
 | 
			
		||||
        retval
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn fold_expr_enum(&mut self, op: ExprEnum) -> Result<ExprEnum, Self::Error> {
 | 
			
		||||
        match op {
 | 
			
		||||
            ExprEnum::EnumLiteral(op) => Ok(match self.get_or_make_enum_type_state(op.ty())? {
 | 
			
		||||
                EnumTypeState::TagEnumAndBody(TagAndBodyType::<DynEnum> { tag, body }) => {
 | 
			
		||||
                    TagAndBodyType::<DynEnum>::builder()
 | 
			
		||||
                        .field_tag(DynEnum::new_by_index(tag, op.variant_index(), None))
 | 
			
		||||
                        .field_body(match op.variant_value() {
 | 
			
		||||
                            Some(variant_value) => variant_value.fold(self)?.cast_to_bits(),
 | 
			
		||||
                            None => DynUInt::with_type(body, 0u8).to_expr(),
 | 
			
		||||
                        })
 | 
			
		||||
                        .build()
 | 
			
		||||
                        .expr_enum()
 | 
			
		||||
                }
 | 
			
		||||
                EnumTypeState::TagUIntAndBody(TagAndBodyType::<DynUInt> { tag, body }) => {
 | 
			
		||||
                    TagAndBodyType::<DynUInt>::builder()
 | 
			
		||||
                        .field_tag(DynUInt::with_type(tag, op.variant_index()))
 | 
			
		||||
                        .field_body(match op.variant_value() {
 | 
			
		||||
                            Some(variant_value) => variant_value.fold(self)?.cast_to_bits(),
 | 
			
		||||
                            None => DynUInt::with_type(body, 0u8).to_expr(),
 | 
			
		||||
                        })
 | 
			
		||||
                        .build()
 | 
			
		||||
                        .expr_enum()
 | 
			
		||||
                }
 | 
			
		||||
                EnumTypeState::UInt(_) => TagAndBodyType::<DynUInt>::builder()
 | 
			
		||||
                    .field_tag(DynUInt::with_type(
 | 
			
		||||
                        DynUIntType::new(op.ty().discriminant_bit_width()),
 | 
			
		||||
                        op.variant_index(),
 | 
			
		||||
                    ))
 | 
			
		||||
                    .field_body(match op.variant_value() {
 | 
			
		||||
                        Some(variant_value) => variant_value.fold(self)?.cast_to_bits(),
 | 
			
		||||
                        None => DynUInt::with_type(
 | 
			
		||||
                            DynUIntType::new(
 | 
			
		||||
                                op.ty().bit_width() - op.ty().discriminant_bit_width(),
 | 
			
		||||
                            ),
 | 
			
		||||
                            0u8,
 | 
			
		||||
                        )
 | 
			
		||||
                        .to_expr(),
 | 
			
		||||
                    })
 | 
			
		||||
                    .build()
 | 
			
		||||
                    .cast_to_bits()
 | 
			
		||||
                    .expr_enum(),
 | 
			
		||||
                EnumTypeState::Unchanged => ExprEnum::EnumLiteral(
 | 
			
		||||
                    ops::EnumLiteral::new_unchecked(
 | 
			
		||||
                        op.variant_value().map(|v| v.fold(self)).transpose()?,
 | 
			
		||||
                        op.variant_index(),
 | 
			
		||||
                        op.ty(),
 | 
			
		||||
                    )
 | 
			
		||||
                    .intern_sized(),
 | 
			
		||||
                ),
 | 
			
		||||
            }),
 | 
			
		||||
            ExprEnum::VariantAccess(op) => {
 | 
			
		||||
                Ok(match self.get_or_make_enum_type_state(op.base().ty())? {
 | 
			
		||||
                    EnumTypeState::TagEnumAndBody(_) | EnumTypeState::TagUIntAndBody(_) => {
 | 
			
		||||
                        match op.variant_type().ty {
 | 
			
		||||
                            Some(_) => op
 | 
			
		||||
                                .base()
 | 
			
		||||
                                .expr_enum()
 | 
			
		||||
                                .fold(self)?
 | 
			
		||||
                                .to_expr()
 | 
			
		||||
                                .with_type::<TagAndBody<DynCanonicalValue>>()
 | 
			
		||||
                                .body[..op.ty().bit_width()]
 | 
			
		||||
                                .cast_bits_to::<DynCanonicalValue>(op.ty())
 | 
			
		||||
                                .expr_enum(),
 | 
			
		||||
                            None => ().to_expr().expr_enum(),
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    EnumTypeState::UInt(_) => match op.variant_type().ty {
 | 
			
		||||
                        Some(_) => {
 | 
			
		||||
                            let base_int = op
 | 
			
		||||
                                .base()
 | 
			
		||||
                                .expr_enum()
 | 
			
		||||
                                .fold(self)?
 | 
			
		||||
                                .to_expr()
 | 
			
		||||
                                .with_type::<DynUInt>();
 | 
			
		||||
                            dbg!(base_int);
 | 
			
		||||
                            let base_ty = op.base().ty();
 | 
			
		||||
                            let ty_bit_width = op.ty().bit_width();
 | 
			
		||||
                            base_int[base_ty.discriminant_bit_width()..][..ty_bit_width]
 | 
			
		||||
                                .cast_bits_to::<DynCanonicalValue>(op.ty())
 | 
			
		||||
                                .expr_enum()
 | 
			
		||||
                        }
 | 
			
		||||
                        None => ().to_expr().expr_enum(),
 | 
			
		||||
                    },
 | 
			
		||||
                    EnumTypeState::Unchanged => match op.variant_type().ty {
 | 
			
		||||
                        Some(_) => ExprEnum::VariantAccess(
 | 
			
		||||
                            ops::VariantAccess::new_unchecked(
 | 
			
		||||
                                op.base().fold(self)?,
 | 
			
		||||
                                op.variant_index(),
 | 
			
		||||
                            )
 | 
			
		||||
                            .intern_sized(),
 | 
			
		||||
                        ),
 | 
			
		||||
                        None => ().to_expr().expr_enum(),
 | 
			
		||||
                    },
 | 
			
		||||
                })
 | 
			
		||||
            }
 | 
			
		||||
            ExprEnum::MemPort(mem_port) => Ok(
 | 
			
		||||
                if let Some(&wire) = self.replacement_mem_ports.get(&mem_port) {
 | 
			
		||||
                    ExprEnum::Wire(wire)
 | 
			
		||||
                } else {
 | 
			
		||||
                    ExprEnum::MemPort(mem_port.fold(self)?)
 | 
			
		||||
                },
 | 
			
		||||
            ),
 | 
			
		||||
            ExprEnum::Literal(_)
 | 
			
		||||
            | ExprEnum::ArrayLiteral(_)
 | 
			
		||||
            | ExprEnum::BundleLiteral(_)
 | 
			
		||||
            | ExprEnum::NotU(_)
 | 
			
		||||
            | ExprEnum::NotS(_)
 | 
			
		||||
            | ExprEnum::Neg(_)
 | 
			
		||||
            | ExprEnum::BitAndU(_)
 | 
			
		||||
            | ExprEnum::BitAndS(_)
 | 
			
		||||
            | ExprEnum::BitOrU(_)
 | 
			
		||||
            | ExprEnum::BitOrS(_)
 | 
			
		||||
            | ExprEnum::BitXorU(_)
 | 
			
		||||
            | ExprEnum::BitXorS(_)
 | 
			
		||||
            | ExprEnum::AddU(_)
 | 
			
		||||
            | ExprEnum::AddS(_)
 | 
			
		||||
            | ExprEnum::SubU(_)
 | 
			
		||||
            | ExprEnum::SubS(_)
 | 
			
		||||
            | ExprEnum::MulU(_)
 | 
			
		||||
            | ExprEnum::MulS(_)
 | 
			
		||||
            | ExprEnum::DynShlU(_)
 | 
			
		||||
            | ExprEnum::DynShlS(_)
 | 
			
		||||
            | ExprEnum::DynShrU(_)
 | 
			
		||||
            | ExprEnum::DynShrS(_)
 | 
			
		||||
            | ExprEnum::FixedShlU(_)
 | 
			
		||||
            | ExprEnum::FixedShlS(_)
 | 
			
		||||
            | ExprEnum::FixedShrU(_)
 | 
			
		||||
            | ExprEnum::FixedShrS(_)
 | 
			
		||||
            | ExprEnum::CmpLtU(_)
 | 
			
		||||
            | ExprEnum::CmpLtS(_)
 | 
			
		||||
            | ExprEnum::CmpLeU(_)
 | 
			
		||||
            | ExprEnum::CmpLeS(_)
 | 
			
		||||
            | ExprEnum::CmpGtU(_)
 | 
			
		||||
            | ExprEnum::CmpGtS(_)
 | 
			
		||||
            | ExprEnum::CmpGeU(_)
 | 
			
		||||
            | ExprEnum::CmpGeS(_)
 | 
			
		||||
            | ExprEnum::CmpEqU(_)
 | 
			
		||||
            | ExprEnum::CmpEqS(_)
 | 
			
		||||
            | ExprEnum::CmpNeU(_)
 | 
			
		||||
            | ExprEnum::CmpNeS(_)
 | 
			
		||||
            | ExprEnum::CastUIntToUInt(_)
 | 
			
		||||
            | ExprEnum::CastUIntToSInt(_)
 | 
			
		||||
            | ExprEnum::CastSIntToUInt(_)
 | 
			
		||||
            | ExprEnum::CastSIntToSInt(_)
 | 
			
		||||
            | ExprEnum::SliceUInt(_)
 | 
			
		||||
            | ExprEnum::SliceSInt(_)
 | 
			
		||||
            | ExprEnum::ReduceBitAnd(_)
 | 
			
		||||
            | ExprEnum::ReduceBitOr(_)
 | 
			
		||||
            | ExprEnum::ReduceBitXor(_)
 | 
			
		||||
            | ExprEnum::FieldAccess(_)
 | 
			
		||||
            | ExprEnum::ArrayIndex(_)
 | 
			
		||||
            | ExprEnum::DynArrayIndex(_)
 | 
			
		||||
            | ExprEnum::CastToBits(_)
 | 
			
		||||
            | ExprEnum::CastBitsTo(_)
 | 
			
		||||
            | ExprEnum::CastBitToClock(_)
 | 
			
		||||
            | ExprEnum::CastBitToSyncReset(_)
 | 
			
		||||
            | ExprEnum::CastBitToAsyncReset(_)
 | 
			
		||||
            | ExprEnum::CastSyncResetToReset(_)
 | 
			
		||||
            | ExprEnum::CastAsyncResetToReset(_)
 | 
			
		||||
            | ExprEnum::CastClockToBit(_)
 | 
			
		||||
            | ExprEnum::CastSyncResetToBit(_)
 | 
			
		||||
            | ExprEnum::CastAsyncResetToBit(_)
 | 
			
		||||
            | ExprEnum::CastResetToBit(_)
 | 
			
		||||
            | ExprEnum::ModuleIO(_)
 | 
			
		||||
            | ExprEnum::Instance(_)
 | 
			
		||||
            | ExprEnum::Wire(_)
 | 
			
		||||
            | ExprEnum::Reg(_) => op.default_fold(self),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn fold_block(&mut self, block: Block) -> Result<Block, Self::Error> {
 | 
			
		||||
        let mut memories = vec![];
 | 
			
		||||
        let mut stmts = vec![];
 | 
			
		||||
        for memory in block.memories {
 | 
			
		||||
            let old_element_ty = *memory.array_type().element();
 | 
			
		||||
            let new_element_ty = memory.array_type().element().fold(self)?;
 | 
			
		||||
            if new_element_ty != old_element_ty {
 | 
			
		||||
                let mut new_ports = vec![];
 | 
			
		||||
                for port in memory.ports() {
 | 
			
		||||
                    let new_port = MemPort::<DynPortType>::new_unchecked(
 | 
			
		||||
                        port.mem_name(),
 | 
			
		||||
                        port.source_location(),
 | 
			
		||||
                        port.port_name(),
 | 
			
		||||
                        port.addr_type(),
 | 
			
		||||
                        new_element_ty,
 | 
			
		||||
                    );
 | 
			
		||||
                    new_ports.push(new_port.intern());
 | 
			
		||||
                    let new_port_ty = new_port.ty();
 | 
			
		||||
                    let mut wire_ty_fields = Vec::from_iter(new_port_ty.fields());
 | 
			
		||||
                    if let Some(wmask_name) = new_port.port_kind().wmask_name() {
 | 
			
		||||
                        let index = *new_port_ty
 | 
			
		||||
                            .name_indexes()
 | 
			
		||||
                            .get(&wmask_name.intern())
 | 
			
		||||
                            .unwrap();
 | 
			
		||||
                        wire_ty_fields[index].ty = port.ty().fields()[index].ty;
 | 
			
		||||
                    }
 | 
			
		||||
                    let wire_ty = DynBundleType::new(Intern::intern_owned(wire_ty_fields));
 | 
			
		||||
                    if wire_ty == new_port_ty {
 | 
			
		||||
                        continue;
 | 
			
		||||
                    }
 | 
			
		||||
                    let wire_name = self.name_id_gen.gen(
 | 
			
		||||
                        (*format!("{}_{}", memory.scoped_name().1 .0, port.port_name())).intern(),
 | 
			
		||||
                    );
 | 
			
		||||
                    let wire = Wire::new_unchecked(
 | 
			
		||||
                        ScopedNameId(memory.scoped_name().0, wire_name),
 | 
			
		||||
                        port.source_location(),
 | 
			
		||||
                        wire_ty,
 | 
			
		||||
                    );
 | 
			
		||||
                    stmts.push(
 | 
			
		||||
                        StmtWire {
 | 
			
		||||
                            annotations: Default::default(),
 | 
			
		||||
                            wire: wire.to_dyn_canonical_wire(),
 | 
			
		||||
                        }
 | 
			
		||||
                        .into(),
 | 
			
		||||
                    );
 | 
			
		||||
                    connect_port(
 | 
			
		||||
                        &mut stmts,
 | 
			
		||||
                        new_port.to_expr().to_canonical_dyn(),
 | 
			
		||||
                        wire.to_expr().to_canonical_dyn(),
 | 
			
		||||
                        port.source_location(),
 | 
			
		||||
                    );
 | 
			
		||||
                    self.replacement_mem_ports
 | 
			
		||||
                        .insert(port, wire.to_dyn_canonical_wire().intern_sized());
 | 
			
		||||
                }
 | 
			
		||||
                memories.push(Mem::new_unchecked(
 | 
			
		||||
                    memory.scoped_name(),
 | 
			
		||||
                    memory.source_location(),
 | 
			
		||||
                    ArrayType::new_slice(new_element_ty, memory.array_type().len()),
 | 
			
		||||
                    memory.initial_value(),
 | 
			
		||||
                    Intern::intern_owned(new_ports),
 | 
			
		||||
                    memory.read_latency(),
 | 
			
		||||
                    memory.write_latency(),
 | 
			
		||||
                    memory.read_under_write(),
 | 
			
		||||
                    memory.port_annotations(),
 | 
			
		||||
                    memory.mem_annotations(),
 | 
			
		||||
                ));
 | 
			
		||||
            } else {
 | 
			
		||||
                memories.push(memory.fold(self)?);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        stmts.extend_from_slice(&block.stmts.fold(self)?);
 | 
			
		||||
        Ok(Block {
 | 
			
		||||
            memories: Intern::intern_owned(memories),
 | 
			
		||||
            stmts: Intern::intern_owned(stmts),
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn fold_stmt(&mut self, stmt: Stmt) -> Result<Stmt, Self::Error> {
 | 
			
		||||
        fn match_int_tag(
 | 
			
		||||
            state: &mut State,
 | 
			
		||||
            int_tag_expr: Expr<DynUInt>,
 | 
			
		||||
            source_location: SourceLocation,
 | 
			
		||||
            blocks: Interned<[Block]>,
 | 
			
		||||
        ) -> Result<StmtIf, SimplifyEnumsError> {
 | 
			
		||||
            let mut blocks_iter = blocks.iter().copied().enumerate();
 | 
			
		||||
            let (_, last_block) = blocks_iter.next_back().unwrap_or_default();
 | 
			
		||||
            let Some((next_to_last_variant_index, next_to_last_block)) = blocks_iter.next_back()
 | 
			
		||||
            else {
 | 
			
		||||
                return Ok(StmtIf {
 | 
			
		||||
                    cond: true.to_expr(),
 | 
			
		||||
                    source_location,
 | 
			
		||||
                    blocks: [last_block.fold(state)?, Block::default()],
 | 
			
		||||
                });
 | 
			
		||||
            };
 | 
			
		||||
            let mut retval = StmtIf {
 | 
			
		||||
                cond: int_tag_expr.cmp_eq(DynUInt::with_type(
 | 
			
		||||
                    int_tag_expr.ty(),
 | 
			
		||||
                    next_to_last_variant_index,
 | 
			
		||||
                )),
 | 
			
		||||
                source_location,
 | 
			
		||||
                blocks: [next_to_last_block.fold(state)?, last_block.fold(state)?],
 | 
			
		||||
            };
 | 
			
		||||
            for (variant_index, block) in blocks_iter.rev() {
 | 
			
		||||
                retval = StmtIf {
 | 
			
		||||
                    cond: int_tag_expr.cmp_eq(DynUInt::with_type(int_tag_expr.ty(), variant_index)),
 | 
			
		||||
                    source_location,
 | 
			
		||||
                    blocks: [
 | 
			
		||||
                        block.fold(state)?,
 | 
			
		||||
                        Block {
 | 
			
		||||
                            memories: Default::default(),
 | 
			
		||||
                            stmts: [Stmt::from(retval)][..].intern(),
 | 
			
		||||
                        },
 | 
			
		||||
                    ],
 | 
			
		||||
                };
 | 
			
		||||
            }
 | 
			
		||||
            Ok(retval)
 | 
			
		||||
        }
 | 
			
		||||
        match stmt {
 | 
			
		||||
            Stmt::Match(StmtMatch {
 | 
			
		||||
                expr,
 | 
			
		||||
                source_location,
 | 
			
		||||
                blocks,
 | 
			
		||||
            }) => match self.get_or_make_enum_type_state(expr.ty())? {
 | 
			
		||||
                EnumTypeState::TagEnumAndBody(_) => Ok(StmtMatch {
 | 
			
		||||
                    expr: expr
 | 
			
		||||
                        .expr_enum()
 | 
			
		||||
                        .fold(self)?
 | 
			
		||||
                        .to_expr()
 | 
			
		||||
                        .with_type::<TagAndBody<DynEnum>>()
 | 
			
		||||
                        .tag,
 | 
			
		||||
                    source_location,
 | 
			
		||||
                    blocks: blocks.fold(self)?,
 | 
			
		||||
                }
 | 
			
		||||
                .into()),
 | 
			
		||||
                EnumTypeState::TagUIntAndBody(_) => {
 | 
			
		||||
                    let int_tag_expr = expr
 | 
			
		||||
                        .expr_enum()
 | 
			
		||||
                        .fold(self)?
 | 
			
		||||
                        .to_expr()
 | 
			
		||||
                        .with_type::<TagAndBody<DynUInt>>()
 | 
			
		||||
                        .tag;
 | 
			
		||||
                    Ok(match_int_tag(self, int_tag_expr, source_location, blocks)?.into())
 | 
			
		||||
                }
 | 
			
		||||
                EnumTypeState::UInt(_) => {
 | 
			
		||||
                    let int_tag_expr = expr
 | 
			
		||||
                        .expr_enum()
 | 
			
		||||
                        .fold(self)?
 | 
			
		||||
                        .to_expr()
 | 
			
		||||
                        .with_type::<DynUInt>()[..expr.ty().discriminant_bit_width()];
 | 
			
		||||
                    Ok(match_int_tag(self, int_tag_expr, source_location, blocks)?.into())
 | 
			
		||||
                }
 | 
			
		||||
                EnumTypeState::Unchanged => Ok(StmtMatch {
 | 
			
		||||
                    expr: expr.fold(self)?,
 | 
			
		||||
                    source_location,
 | 
			
		||||
                    blocks: blocks.fold(self)?,
 | 
			
		||||
                }
 | 
			
		||||
                .into()),
 | 
			
		||||
            },
 | 
			
		||||
            Stmt::Connect(_) | Stmt::If(_) | Stmt::Declaration(_) => stmt.default_fold(self),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn fold_stmt_match(&mut self, _v: StmtMatch) -> Result<StmtMatch, Self::Error> {
 | 
			
		||||
        unreachable!()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn fold_type_enum(&mut self, type_enum: TypeEnum) -> Result<TypeEnum, Self::Error> {
 | 
			
		||||
        match type_enum {
 | 
			
		||||
            TypeEnum::EnumType(enum_type) => {
 | 
			
		||||
                Ok(match self.get_or_make_enum_type_state(enum_type)? {
 | 
			
		||||
                    EnumTypeState::TagEnumAndBody(ty) => TypeEnum::BundleType(ty.canonical()),
 | 
			
		||||
                    EnumTypeState::TagUIntAndBody(ty) => TypeEnum::BundleType(ty.canonical()),
 | 
			
		||||
                    EnumTypeState::UInt(ty) => TypeEnum::UInt(ty),
 | 
			
		||||
                    EnumTypeState::Unchanged => TypeEnum::EnumType(enum_type),
 | 
			
		||||
                })
 | 
			
		||||
            }
 | 
			
		||||
            TypeEnum::BundleType(_)
 | 
			
		||||
            | TypeEnum::ArrayType(_)
 | 
			
		||||
            | TypeEnum::UInt(_)
 | 
			
		||||
            | TypeEnum::SInt(_)
 | 
			
		||||
            | TypeEnum::Clock(_)
 | 
			
		||||
            | TypeEnum::AsyncReset(_)
 | 
			
		||||
            | TypeEnum::SyncReset(_)
 | 
			
		||||
            | TypeEnum::Reset(_) => type_enum.default_fold(self),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn fold_value_enum(&mut self, value_enum: ValueEnum) -> Result<ValueEnum, Self::Error> {
 | 
			
		||||
        match value_enum {
 | 
			
		||||
            ValueEnum::Enum(enum_value) => {
 | 
			
		||||
                Ok(match self.get_or_make_enum_type_state(enum_value.ty())? {
 | 
			
		||||
                    EnumTypeState::TagEnumAndBody(TagAndBodyType::<DynEnum> {
 | 
			
		||||
                        tag: tag_ty,
 | 
			
		||||
                        body: body_ty,
 | 
			
		||||
                    }) => ValueEnum::Bundle(
 | 
			
		||||
                        TagAndBody {
 | 
			
		||||
                            tag: DynEnum::new_by_index(tag_ty, enum_value.variant_index(), None),
 | 
			
		||||
                            body: value_to_uint(enum_value.variant_value().as_ref(), body_ty),
 | 
			
		||||
                        }
 | 
			
		||||
                        .to_canonical(),
 | 
			
		||||
                    ),
 | 
			
		||||
                    EnumTypeState::TagUIntAndBody(TagAndBodyType::<DynUInt> {
 | 
			
		||||
                        tag: tag_ty,
 | 
			
		||||
                        body: body_ty,
 | 
			
		||||
                    }) => ValueEnum::Bundle(
 | 
			
		||||
                        TagAndBody {
 | 
			
		||||
                            tag: DynUInt::with_type(tag_ty, enum_value.variant_index()),
 | 
			
		||||
                            body: value_to_uint(enum_value.variant_value().as_ref(), body_ty),
 | 
			
		||||
                        }
 | 
			
		||||
                        .to_canonical(),
 | 
			
		||||
                    ),
 | 
			
		||||
                    EnumTypeState::UInt(target_ty) => {
 | 
			
		||||
                        ValueEnum::UInt(value_to_uint(Some(&enum_value), target_ty))
 | 
			
		||||
                    }
 | 
			
		||||
                    EnumTypeState::Unchanged => ValueEnum::Enum(enum_value),
 | 
			
		||||
                })
 | 
			
		||||
            }
 | 
			
		||||
            ValueEnum::Bundle(_)
 | 
			
		||||
            | ValueEnum::Array(_)
 | 
			
		||||
            | ValueEnum::UInt(_)
 | 
			
		||||
            | ValueEnum::SInt(_)
 | 
			
		||||
            | ValueEnum::Clock(_)
 | 
			
		||||
            | ValueEnum::AsyncReset(_)
 | 
			
		||||
            | ValueEnum::SyncReset(_)
 | 
			
		||||
            | ValueEnum::Reset(_) => value_enum.default_fold(self),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn fold_variant_type<T>(&mut self, _v: VariantType<T>) -> Result<VariantType<T>, Self::Error> {
 | 
			
		||||
        unreachable!()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn fold_enum_literal<EnumTy>(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        _v: ops::EnumLiteral<EnumTy>,
 | 
			
		||||
    ) -> Result<ops::EnumLiteral<EnumTy>, Self::Error>
 | 
			
		||||
    where
 | 
			
		||||
        EnumTy: EnumType,
 | 
			
		||||
        EnumTy::Value: EnumValue<Type = EnumTy>,
 | 
			
		||||
    {
 | 
			
		||||
        unreachable!()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn fold_variant_access<T, VariantTy>(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        _v: ops::VariantAccess<T, VariantTy>,
 | 
			
		||||
    ) -> Result<ops::VariantAccess<T, VariantTy>, Self::Error>
 | 
			
		||||
    where
 | 
			
		||||
        T: EnumType,
 | 
			
		||||
        T::Value: EnumValue,
 | 
			
		||||
        VariantTy: Type,
 | 
			
		||||
    {
 | 
			
		||||
        unreachable!()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
 | 
			
		||||
pub enum SimplifyEnumsKind {
 | 
			
		||||
    SimplifyToEnumsWithNoBody,
 | 
			
		||||
    ReplaceWithBundleOfUInts,
 | 
			
		||||
    ReplaceWithUInt,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn simplify_enums(
 | 
			
		||||
    module: Interned<Module<DynBundle>>,
 | 
			
		||||
    kind: SimplifyEnumsKind,
 | 
			
		||||
) -> Result<Interned<Module<DynBundle>>, SimplifyEnumsError> {
 | 
			
		||||
    module.fold(&mut State {
 | 
			
		||||
        enum_types: HashMap::new(),
 | 
			
		||||
        replacement_mem_ports: HashMap::new(),
 | 
			
		||||
        kind,
 | 
			
		||||
        name_id_gen: NameIdGen::default(),
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										940
									
								
								crates/fayalite/src/module/transform/simplify_memories.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										940
									
								
								crates/fayalite/src/module/transform/simplify_memories.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,940 @@
 | 
			
		|||
// SPDX-License-Identifier: LGPL-3.0-or-later
 | 
			
		||||
// See Notices.txt for copyright information
 | 
			
		||||
use crate::{
 | 
			
		||||
    annotations::TargetedAnnotation,
 | 
			
		||||
    array::{Array, ArrayType, ValueArrayOrSlice},
 | 
			
		||||
    bundle::{BundleType, BundleValue, DynBundle},
 | 
			
		||||
    expr::{Expr, ExprEnum, ToExpr},
 | 
			
		||||
    int::{DynSInt, DynSIntType, DynUInt, DynUIntType},
 | 
			
		||||
    intern::{Intern, Interned},
 | 
			
		||||
    memory::{Mem, MemPort, PortType},
 | 
			
		||||
    module::{
 | 
			
		||||
        transform::visit::{Fold, Folder},
 | 
			
		||||
        Block, Module, NameId, NameIdGen, ScopedNameId, Stmt, StmtConnect, StmtWire,
 | 
			
		||||
    },
 | 
			
		||||
    source_location::SourceLocation,
 | 
			
		||||
    ty::{DynCanonicalValue, DynType, Type, TypeEnum},
 | 
			
		||||
    util::MakeMutSlice,
 | 
			
		||||
    wire::Wire,
 | 
			
		||||
};
 | 
			
		||||
use bitvec::{slice::BitSlice, vec::BitVec};
 | 
			
		||||
use hashbrown::HashMap;
 | 
			
		||||
use std::{
 | 
			
		||||
    convert::Infallible,
 | 
			
		||||
    fmt::Write,
 | 
			
		||||
    ops::{Deref, DerefMut},
 | 
			
		||||
    rc::Rc,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
 | 
			
		||||
enum SingleType {
 | 
			
		||||
    UInt(DynUIntType),
 | 
			
		||||
    SInt(DynSIntType),
 | 
			
		||||
    UIntArray(ArrayType<[DynUInt]>),
 | 
			
		||||
    SIntArray(ArrayType<[DynSInt]>),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl SingleType {
 | 
			
		||||
    fn is_array_type(self, array_type: ArrayType<[DynCanonicalValue]>) -> bool {
 | 
			
		||||
        match self {
 | 
			
		||||
            SingleType::UInt(_) | SingleType::SInt(_) => false,
 | 
			
		||||
            SingleType::UIntArray(ty) => ty.canonical() == array_type,
 | 
			
		||||
            SingleType::SIntArray(ty) => ty.canonical() == array_type,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    fn array_len(self) -> usize {
 | 
			
		||||
        match self {
 | 
			
		||||
            SingleType::UInt(_ty) => 1,
 | 
			
		||||
            SingleType::SInt(_ty) => 1,
 | 
			
		||||
            SingleType::UIntArray(ty) => ty.len(),
 | 
			
		||||
            SingleType::SIntArray(ty) => ty.len(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug)]
 | 
			
		||||
enum MemSplit {
 | 
			
		||||
    Bundle {
 | 
			
		||||
        fields: Rc<[MemSplit]>,
 | 
			
		||||
    },
 | 
			
		||||
    Single {
 | 
			
		||||
        output_mem: Option<Mem<[DynCanonicalValue]>>,
 | 
			
		||||
        element_type: SingleType,
 | 
			
		||||
        unchanged_element_type: bool,
 | 
			
		||||
    },
 | 
			
		||||
    Array {
 | 
			
		||||
        elements: Rc<[MemSplit]>,
 | 
			
		||||
    },
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl MemSplit {
 | 
			
		||||
    fn mark_changed_element_type(self) -> Self {
 | 
			
		||||
        match self {
 | 
			
		||||
            MemSplit::Bundle { fields: _ } => self,
 | 
			
		||||
            MemSplit::Single {
 | 
			
		||||
                output_mem,
 | 
			
		||||
                element_type,
 | 
			
		||||
                unchanged_element_type: _,
 | 
			
		||||
            } => MemSplit::Single {
 | 
			
		||||
                output_mem,
 | 
			
		||||
                element_type,
 | 
			
		||||
                unchanged_element_type: false,
 | 
			
		||||
            },
 | 
			
		||||
            MemSplit::Array { elements: _ } => self,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    fn new(element_type: TypeEnum) -> Self {
 | 
			
		||||
        match element_type {
 | 
			
		||||
            TypeEnum::BundleType(bundle_ty) => MemSplit::Bundle {
 | 
			
		||||
                fields: bundle_ty
 | 
			
		||||
                    .fields()
 | 
			
		||||
                    .into_iter()
 | 
			
		||||
                    .map(|field| Self::new(field.ty.type_enum()).mark_changed_element_type())
 | 
			
		||||
                    .collect(),
 | 
			
		||||
            },
 | 
			
		||||
            TypeEnum::ArrayType(ty) => {
 | 
			
		||||
                let element = MemSplit::new(ty.element().type_enum());
 | 
			
		||||
                if let Self::Single {
 | 
			
		||||
                    output_mem: _,
 | 
			
		||||
                    element_type,
 | 
			
		||||
                    unchanged_element_type,
 | 
			
		||||
                } = element
 | 
			
		||||
                {
 | 
			
		||||
                    match element_type {
 | 
			
		||||
                        SingleType::UInt(element_type) => Self::Single {
 | 
			
		||||
                            output_mem: None,
 | 
			
		||||
                            element_type: SingleType::UIntArray(ArrayType::new_slice(
 | 
			
		||||
                                element_type,
 | 
			
		||||
                                ty.len(),
 | 
			
		||||
                            )),
 | 
			
		||||
                            unchanged_element_type,
 | 
			
		||||
                        },
 | 
			
		||||
                        SingleType::SInt(element_type) => Self::Single {
 | 
			
		||||
                            output_mem: None,
 | 
			
		||||
                            element_type: SingleType::SIntArray(ArrayType::new_slice(
 | 
			
		||||
                                element_type,
 | 
			
		||||
                                ty.len(),
 | 
			
		||||
                            )),
 | 
			
		||||
                            unchanged_element_type,
 | 
			
		||||
                        },
 | 
			
		||||
                        SingleType::UIntArray(element_type) => Self::Single {
 | 
			
		||||
                            output_mem: None,
 | 
			
		||||
                            element_type: SingleType::UIntArray(ArrayType::new_slice(
 | 
			
		||||
                                *element_type.element(),
 | 
			
		||||
                                ty.len()
 | 
			
		||||
                                    .checked_mul(element_type.len())
 | 
			
		||||
                                    .expect("memory element type can't be too big"),
 | 
			
		||||
                            )),
 | 
			
		||||
                            unchanged_element_type: false,
 | 
			
		||||
                        },
 | 
			
		||||
                        SingleType::SIntArray(element_type) => Self::Single {
 | 
			
		||||
                            output_mem: None,
 | 
			
		||||
                            element_type: SingleType::SIntArray(ArrayType::new_slice(
 | 
			
		||||
                                *element_type.element(),
 | 
			
		||||
                                ty.len()
 | 
			
		||||
                                    .checked_mul(element_type.len())
 | 
			
		||||
                                    .expect("memory element type can't be too big"),
 | 
			
		||||
                            )),
 | 
			
		||||
                            unchanged_element_type: false,
 | 
			
		||||
                        },
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    let element = element.mark_changed_element_type();
 | 
			
		||||
                    Self::Array {
 | 
			
		||||
                        elements: (0..ty.len()).map(|_| element.clone()).collect(),
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            TypeEnum::UInt(ty) => Self::Single {
 | 
			
		||||
                output_mem: None,
 | 
			
		||||
                element_type: SingleType::UInt(ty),
 | 
			
		||||
                unchanged_element_type: true,
 | 
			
		||||
            },
 | 
			
		||||
            TypeEnum::SInt(ty) => Self::Single {
 | 
			
		||||
                output_mem: None,
 | 
			
		||||
                element_type: SingleType::SInt(ty),
 | 
			
		||||
                unchanged_element_type: true,
 | 
			
		||||
            },
 | 
			
		||||
            TypeEnum::EnumType(ty) => Self::Single {
 | 
			
		||||
                output_mem: None,
 | 
			
		||||
                element_type: SingleType::UInt(DynUIntType::new(ty.bit_width())),
 | 
			
		||||
                unchanged_element_type: false,
 | 
			
		||||
            },
 | 
			
		||||
            TypeEnum::Clock(_)
 | 
			
		||||
            | TypeEnum::AsyncReset(_)
 | 
			
		||||
            | TypeEnum::SyncReset(_)
 | 
			
		||||
            | TypeEnum::Reset(_) => unreachable!("memory element type is a storable type"),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct MemState {
 | 
			
		||||
    replacement_ports: Box<[ExprEnum]>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct SplitState<'a> {
 | 
			
		||||
    wire_rdata: Box<[Option<Expr<DynCanonicalValue>>]>,
 | 
			
		||||
    wire_wdata: Box<[Option<Expr<DynCanonicalValue>>]>,
 | 
			
		||||
    wire_wmask: Box<[Option<Expr<DynCanonicalValue>>]>,
 | 
			
		||||
    initial_value: Option<Box<[&'a BitSlice]>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> SplitState<'a> {
 | 
			
		||||
    fn placeholder() -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            wire_rdata: Box::new([]),
 | 
			
		||||
            wire_wdata: Box::new([]),
 | 
			
		||||
            wire_wmask: Box::new([]),
 | 
			
		||||
            initial_value: None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    fn new_empty(ports_len: usize) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            wire_rdata: (0..ports_len).map(|_| None).collect(),
 | 
			
		||||
            wire_wdata: (0..ports_len).map(|_| None).collect(),
 | 
			
		||||
            wire_wmask: (0..ports_len).map(|_| None).collect(),
 | 
			
		||||
            initial_value: None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct SplitStateStack<'a> {
 | 
			
		||||
    ports_len: usize,
 | 
			
		||||
    values: Vec<SplitState<'a>>,
 | 
			
		||||
    top_index: usize,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> SplitStateStack<'a> {
 | 
			
		||||
    fn new(ports_len: usize, value: SplitState<'a>) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            ports_len,
 | 
			
		||||
            values: vec![value],
 | 
			
		||||
            top_index: 0,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    fn top(&mut self) -> &mut SplitState<'a> {
 | 
			
		||||
        &mut self.values[self.top_index]
 | 
			
		||||
    }
 | 
			
		||||
    fn pop(&mut self) {
 | 
			
		||||
        self.top_index = self
 | 
			
		||||
            .top_index
 | 
			
		||||
            .checked_sub(1)
 | 
			
		||||
            .expect("there's always at least one entry in the stack");
 | 
			
		||||
    }
 | 
			
		||||
    fn push_map(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        mut wire_map: impl FnMut(Expr<DynCanonicalValue>) -> Expr<DynCanonicalValue>,
 | 
			
		||||
        mut initial_value_element_map: impl FnMut(&BitSlice) -> &BitSlice,
 | 
			
		||||
    ) {
 | 
			
		||||
        let top_index = self.top_index + 1;
 | 
			
		||||
        let mut top = match self.values.get_mut(top_index) {
 | 
			
		||||
            Some(top) => std::mem::replace(top, SplitState::placeholder()),
 | 
			
		||||
            None => SplitState::new_empty(self.ports_len),
 | 
			
		||||
        };
 | 
			
		||||
        for (l, &r) in top.wire_rdata.iter_mut().zip(self.top().wire_rdata.iter()) {
 | 
			
		||||
            *l = r.map(&mut wire_map);
 | 
			
		||||
        }
 | 
			
		||||
        for (l, &r) in top.wire_wdata.iter_mut().zip(self.top().wire_wdata.iter()) {
 | 
			
		||||
            *l = r.map(&mut wire_map);
 | 
			
		||||
        }
 | 
			
		||||
        for (l, &r) in top.wire_wmask.iter_mut().zip(self.top().wire_wmask.iter()) {
 | 
			
		||||
            *l = r.map(&mut wire_map);
 | 
			
		||||
        }
 | 
			
		||||
        if let Some(initial_value) = &self.top().initial_value {
 | 
			
		||||
            let new_initial_value = top.initial_value.get_or_insert_with(|| {
 | 
			
		||||
                Box::from_iter((0..initial_value.len()).map(|_| Default::default()))
 | 
			
		||||
            });
 | 
			
		||||
            for (l, &r) in new_initial_value.iter_mut().zip(initial_value.iter()) {
 | 
			
		||||
                *l = initial_value_element_map(r);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        self.top_index = top_index;
 | 
			
		||||
        if let Some(v) = self.values.get_mut(top_index) {
 | 
			
		||||
            *v = top;
 | 
			
		||||
        } else {
 | 
			
		||||
            assert_eq!(top_index, self.values.len());
 | 
			
		||||
            self.values.push(top);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct SplitMemState<'a, 'b> {
 | 
			
		||||
    module_state: &'a mut ModuleState,
 | 
			
		||||
    input_mem: Mem<[DynCanonicalValue]>,
 | 
			
		||||
    output_mems: &'a mut Vec<Mem<[DynCanonicalValue]>>,
 | 
			
		||||
    output_stmts: &'a mut Vec<Stmt>,
 | 
			
		||||
    element_type: TypeEnum,
 | 
			
		||||
    split: &'a mut MemSplit,
 | 
			
		||||
    mem_name_path: &'a mut String,
 | 
			
		||||
    split_state_stack: &'a mut SplitStateStack<'b>,
 | 
			
		||||
    mem_state: &'a MemState,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl SplitMemState<'_, '_> {
 | 
			
		||||
    fn split_mem(self) {
 | 
			
		||||
        let outer_mem_name_path_len = self.mem_name_path.len();
 | 
			
		||||
        match self.split {
 | 
			
		||||
            MemSplit::Bundle { fields } => {
 | 
			
		||||
                let TypeEnum::BundleType(bundle_type) = self.element_type else {
 | 
			
		||||
                    unreachable!();
 | 
			
		||||
                };
 | 
			
		||||
                for ((field, field_offset), split) in bundle_type
 | 
			
		||||
                    .fields()
 | 
			
		||||
                    .into_iter()
 | 
			
		||||
                    .zip(bundle_type.field_offsets())
 | 
			
		||||
                    .zip(fields.make_mut_slice())
 | 
			
		||||
                {
 | 
			
		||||
                    self.mem_name_path.truncate(outer_mem_name_path_len);
 | 
			
		||||
                    self.mem_name_path.push('_');
 | 
			
		||||
                    self.mem_name_path.push_str(&field.name);
 | 
			
		||||
                    let field_ty_bit_width = field.ty.bit_width();
 | 
			
		||||
                    self.split_state_stack.push_map(
 | 
			
		||||
                        |e: Expr<DynCanonicalValue>| e.with_type::<DynBundle>().field(&field.name),
 | 
			
		||||
                        |initial_value_element| {
 | 
			
		||||
                            &initial_value_element[field_offset..][..field_ty_bit_width]
 | 
			
		||||
                        },
 | 
			
		||||
                    );
 | 
			
		||||
                    SplitMemState {
 | 
			
		||||
                        module_state: self.module_state,
 | 
			
		||||
                        input_mem: self.input_mem,
 | 
			
		||||
                        output_mems: self.output_mems,
 | 
			
		||||
                        output_stmts: self.output_stmts,
 | 
			
		||||
                        element_type: field.ty.type_enum(),
 | 
			
		||||
                        split,
 | 
			
		||||
                        mem_name_path: self.mem_name_path,
 | 
			
		||||
                        split_state_stack: self.split_state_stack,
 | 
			
		||||
                        mem_state: self.mem_state,
 | 
			
		||||
                    }
 | 
			
		||||
                    .split_mem();
 | 
			
		||||
                    self.split_state_stack.pop();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            MemSplit::Single {
 | 
			
		||||
                output_mem,
 | 
			
		||||
                element_type: single_type,
 | 
			
		||||
                unchanged_element_type: _,
 | 
			
		||||
            } => {
 | 
			
		||||
                let new_mem = self.module_state.create_split_mem(
 | 
			
		||||
                    self.input_mem,
 | 
			
		||||
                    self.output_stmts,
 | 
			
		||||
                    self.element_type,
 | 
			
		||||
                    *single_type,
 | 
			
		||||
                    self.mem_name_path,
 | 
			
		||||
                    self.split_state_stack.top(),
 | 
			
		||||
                );
 | 
			
		||||
                for (port, wire) in new_mem
 | 
			
		||||
                    .ports()
 | 
			
		||||
                    .into_iter()
 | 
			
		||||
                    .zip(self.mem_state.replacement_ports.iter())
 | 
			
		||||
                {
 | 
			
		||||
                    let port_expr = port.to_expr();
 | 
			
		||||
                    let wire_expr = Expr::<DynBundle>::new_unchecked(*wire);
 | 
			
		||||
                    for name in [
 | 
			
		||||
                        Some("addr"),
 | 
			
		||||
                        Some("clk"),
 | 
			
		||||
                        Some("en"),
 | 
			
		||||
                        port.port_kind().wmode_name(),
 | 
			
		||||
                    ] {
 | 
			
		||||
                        let Some(name) = name else {
 | 
			
		||||
                            continue;
 | 
			
		||||
                        };
 | 
			
		||||
                        self.output_stmts.push(
 | 
			
		||||
                            StmtConnect {
 | 
			
		||||
                                lhs: port_expr.field(name),
 | 
			
		||||
                                rhs: wire_expr.field(name),
 | 
			
		||||
                                source_location: port.source_location(),
 | 
			
		||||
                            }
 | 
			
		||||
                            .into(),
 | 
			
		||||
                        );
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                *output_mem = Some(new_mem);
 | 
			
		||||
                self.output_mems.push(new_mem);
 | 
			
		||||
            }
 | 
			
		||||
            MemSplit::Array { elements } => {
 | 
			
		||||
                let TypeEnum::ArrayType(array_type) = self.element_type else {
 | 
			
		||||
                    unreachable!();
 | 
			
		||||
                };
 | 
			
		||||
                let element_type = array_type.element().type_enum();
 | 
			
		||||
                let element_bit_width = array_type.element().bit_width();
 | 
			
		||||
                for (index, split) in elements.make_mut_slice().iter_mut().enumerate() {
 | 
			
		||||
                    self.mem_name_path.truncate(outer_mem_name_path_len);
 | 
			
		||||
                    write!(self.mem_name_path, "_{index}").unwrap();
 | 
			
		||||
                    self.split_state_stack.push_map(
 | 
			
		||||
                        |e: Expr<DynCanonicalValue>| {
 | 
			
		||||
                            e.with_type::<Array<[DynCanonicalValue]>>()[index]
 | 
			
		||||
                        },
 | 
			
		||||
                        |initial_value_element| {
 | 
			
		||||
                            &initial_value_element[index * element_bit_width..][..element_bit_width]
 | 
			
		||||
                        },
 | 
			
		||||
                    );
 | 
			
		||||
                    SplitMemState {
 | 
			
		||||
                        module_state: self.module_state,
 | 
			
		||||
                        input_mem: self.input_mem,
 | 
			
		||||
                        output_mems: self.output_mems,
 | 
			
		||||
                        output_stmts: self.output_stmts,
 | 
			
		||||
                        element_type,
 | 
			
		||||
                        split,
 | 
			
		||||
                        mem_name_path: self.mem_name_path,
 | 
			
		||||
                        split_state_stack: self.split_state_stack,
 | 
			
		||||
                        mem_state: self.mem_state,
 | 
			
		||||
                    }
 | 
			
		||||
                    .split_mem();
 | 
			
		||||
                    self.split_state_stack.pop();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct ModuleState {
 | 
			
		||||
    output_module: Option<Interned<Module<DynBundle>>>,
 | 
			
		||||
    name_id_gen: NameIdGen,
 | 
			
		||||
    memories: HashMap<ScopedNameId, MemState>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ModuleState {
 | 
			
		||||
    #[allow(clippy::too_many_arguments)]
 | 
			
		||||
    fn connect_split_mem_port_arrays(
 | 
			
		||||
        output_stmts: &mut Vec<Stmt>,
 | 
			
		||||
        input_array_types: &[ArrayType<[DynCanonicalValue]>],
 | 
			
		||||
        memory_element_array_range_start: usize,
 | 
			
		||||
        memory_element_array_range_len: usize,
 | 
			
		||||
        wire_rdata: Option<Expr<DynCanonicalValue>>,
 | 
			
		||||
        wire_wdata: Option<Expr<DynCanonicalValue>>,
 | 
			
		||||
        wire_wmask: Option<Expr<DynCanonicalValue>>,
 | 
			
		||||
        port_rdata: Option<Expr<Array<[DynCanonicalValue]>>>,
 | 
			
		||||
        port_wdata: Option<Expr<Array<[DynCanonicalValue]>>>,
 | 
			
		||||
        port_wmask: Option<Expr<Array<[DynCanonicalValue]>>>,
 | 
			
		||||
        connect_rdata: impl Copy + Fn(&mut Vec<Stmt>, Expr<DynCanonicalValue>, Expr<DynCanonicalValue>),
 | 
			
		||||
        connect_wdata: impl Copy + Fn(&mut Vec<Stmt>, Expr<DynCanonicalValue>, Expr<DynCanonicalValue>),
 | 
			
		||||
        connect_wmask: impl Copy + Fn(&mut Vec<Stmt>, Expr<DynCanonicalValue>, Expr<DynCanonicalValue>),
 | 
			
		||||
    ) {
 | 
			
		||||
        let Some((input_array_type, input_array_types_rest)) = input_array_types.split_first()
 | 
			
		||||
        else {
 | 
			
		||||
            assert_eq!(memory_element_array_range_len, 1);
 | 
			
		||||
            if let (Some(wire), Some(port)) = (wire_rdata, port_rdata) {
 | 
			
		||||
                connect_rdata(output_stmts, wire, port[memory_element_array_range_start]);
 | 
			
		||||
            }
 | 
			
		||||
            if let (Some(wire), Some(port)) = (wire_wdata, port_wdata) {
 | 
			
		||||
                connect_wdata(output_stmts, wire, port[memory_element_array_range_start]);
 | 
			
		||||
            }
 | 
			
		||||
            if let (Some(wire), Some(port)) = (wire_wmask, port_wmask) {
 | 
			
		||||
                connect_wmask(output_stmts, wire, port[memory_element_array_range_start]);
 | 
			
		||||
            }
 | 
			
		||||
            return;
 | 
			
		||||
        };
 | 
			
		||||
        if input_array_type.is_empty() {
 | 
			
		||||
            return; // no need to connect zero-length arrays, also avoids division by zero
 | 
			
		||||
        }
 | 
			
		||||
        assert_eq!(memory_element_array_range_len % input_array_type.len(), 0);
 | 
			
		||||
        let chunk_size = memory_element_array_range_len / input_array_type.len();
 | 
			
		||||
        for index in 0..input_array_type.len() {
 | 
			
		||||
            let map =
 | 
			
		||||
                |e: Expr<DynCanonicalValue>| e.with_type::<Array<[DynCanonicalValue]>>()[index];
 | 
			
		||||
            let wire_rdata = wire_rdata.map(map);
 | 
			
		||||
            let wire_wdata = wire_wdata.map(map);
 | 
			
		||||
            let wire_wmask = wire_wmask.map(map);
 | 
			
		||||
            Self::connect_split_mem_port_arrays(
 | 
			
		||||
                output_stmts,
 | 
			
		||||
                input_array_types_rest,
 | 
			
		||||
                memory_element_array_range_start + chunk_size * index,
 | 
			
		||||
                chunk_size,
 | 
			
		||||
                wire_rdata,
 | 
			
		||||
                wire_wdata,
 | 
			
		||||
                wire_wmask,
 | 
			
		||||
                port_rdata,
 | 
			
		||||
                port_wdata,
 | 
			
		||||
                port_wmask,
 | 
			
		||||
                connect_rdata,
 | 
			
		||||
                connect_wdata,
 | 
			
		||||
                connect_wmask,
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    #[allow(clippy::too_many_arguments)]
 | 
			
		||||
    fn connect_split_mem_port(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        output_stmts: &mut Vec<Stmt>,
 | 
			
		||||
        mut input_element_type: TypeEnum,
 | 
			
		||||
        single_type: SingleType,
 | 
			
		||||
        source_location: SourceLocation,
 | 
			
		||||
        wire_rdata: Option<Expr<DynCanonicalValue>>,
 | 
			
		||||
        wire_wdata: Option<Expr<DynCanonicalValue>>,
 | 
			
		||||
        wire_wmask: Option<Expr<DynCanonicalValue>>,
 | 
			
		||||
        port_rdata: Option<Expr<DynCanonicalValue>>,
 | 
			
		||||
        port_wdata: Option<Expr<DynCanonicalValue>>,
 | 
			
		||||
        port_wmask: Option<Expr<DynCanonicalValue>>,
 | 
			
		||||
    ) {
 | 
			
		||||
        let mut input_array_types = vec![];
 | 
			
		||||
        let connect_read = |output_stmts: &mut Vec<Stmt>,
 | 
			
		||||
                            wire_read: Expr<DynCanonicalValue>,
 | 
			
		||||
                            port_read: Expr<DynCanonicalValue>| {
 | 
			
		||||
            output_stmts.push(
 | 
			
		||||
                StmtConnect {
 | 
			
		||||
                    lhs: wire_read,
 | 
			
		||||
                    rhs: port_read,
 | 
			
		||||
                    source_location,
 | 
			
		||||
                }
 | 
			
		||||
                .into(),
 | 
			
		||||
            );
 | 
			
		||||
        };
 | 
			
		||||
        let connect_write = |output_stmts: &mut Vec<Stmt>,
 | 
			
		||||
                             wire_write: Expr<DynCanonicalValue>,
 | 
			
		||||
                             port_write: Expr<DynCanonicalValue>| {
 | 
			
		||||
            output_stmts.push(
 | 
			
		||||
                StmtConnect {
 | 
			
		||||
                    lhs: port_write,
 | 
			
		||||
                    rhs: wire_write,
 | 
			
		||||
                    source_location,
 | 
			
		||||
                }
 | 
			
		||||
                .into(),
 | 
			
		||||
            );
 | 
			
		||||
        };
 | 
			
		||||
        let connect_read_enum =
 | 
			
		||||
            |output_stmts: &mut Vec<Stmt>,
 | 
			
		||||
             wire_read: Expr<DynCanonicalValue>,
 | 
			
		||||
             port_read: Expr<DynCanonicalValue>| {
 | 
			
		||||
                connect_read(
 | 
			
		||||
                    output_stmts,
 | 
			
		||||
                    wire_read,
 | 
			
		||||
                    port_read
 | 
			
		||||
                        .with_type::<DynUInt>()
 | 
			
		||||
                        .cast_bits_to(wire_read.ty()),
 | 
			
		||||
                );
 | 
			
		||||
            };
 | 
			
		||||
        let connect_write_enum =
 | 
			
		||||
            |output_stmts: &mut Vec<Stmt>,
 | 
			
		||||
             wire_write: Expr<DynCanonicalValue>,
 | 
			
		||||
             port_write: Expr<DynCanonicalValue>| {
 | 
			
		||||
                connect_write(
 | 
			
		||||
                    output_stmts,
 | 
			
		||||
                    wire_write.cast_to_bits().to_canonical_dyn(),
 | 
			
		||||
                    port_write,
 | 
			
		||||
                );
 | 
			
		||||
            };
 | 
			
		||||
        loop {
 | 
			
		||||
            match input_element_type {
 | 
			
		||||
                TypeEnum::BundleType(_) => unreachable!("bundle types are always split"),
 | 
			
		||||
                TypeEnum::EnumType(_)
 | 
			
		||||
                    if input_array_types
 | 
			
		||||
                        .first()
 | 
			
		||||
                        .map(|&v| single_type.is_array_type(v))
 | 
			
		||||
                        .unwrap_or(true) =>
 | 
			
		||||
                {
 | 
			
		||||
                    if let (Some(wire_rdata), Some(port_rdata)) = (wire_rdata, port_rdata) {
 | 
			
		||||
                        connect_read_enum(output_stmts, wire_rdata, port_rdata);
 | 
			
		||||
                    }
 | 
			
		||||
                    if let (Some(wire_wdata), Some(port_wdata)) = (wire_wdata, port_wdata) {
 | 
			
		||||
                        connect_write_enum(output_stmts, wire_wdata, port_wdata);
 | 
			
		||||
                    }
 | 
			
		||||
                    if let (Some(wire_wmask), Some(port_wmask)) = (wire_wmask, port_wmask) {
 | 
			
		||||
                        connect_write(output_stmts, wire_wmask, port_wmask);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                TypeEnum::EnumType(_) => Self::connect_split_mem_port_arrays(
 | 
			
		||||
                    output_stmts,
 | 
			
		||||
                    &input_array_types,
 | 
			
		||||
                    0,
 | 
			
		||||
                    single_type.array_len(),
 | 
			
		||||
                    wire_rdata,
 | 
			
		||||
                    wire_wdata,
 | 
			
		||||
                    wire_wmask,
 | 
			
		||||
                    port_rdata.map(|e: Expr<DynCanonicalValue>| {
 | 
			
		||||
                        e.with_type::<Array<[DynCanonicalValue]>>()
 | 
			
		||||
                    }),
 | 
			
		||||
                    port_wdata.map(|e: Expr<DynCanonicalValue>| {
 | 
			
		||||
                        e.with_type::<Array<[DynCanonicalValue]>>()
 | 
			
		||||
                    }),
 | 
			
		||||
                    port_wmask.map(|e: Expr<DynCanonicalValue>| {
 | 
			
		||||
                        e.with_type::<Array<[DynCanonicalValue]>>()
 | 
			
		||||
                    }),
 | 
			
		||||
                    connect_read_enum,
 | 
			
		||||
                    connect_write_enum,
 | 
			
		||||
                    connect_write_enum,
 | 
			
		||||
                ),
 | 
			
		||||
                TypeEnum::ArrayType(array_type) => {
 | 
			
		||||
                    input_array_types.push(array_type);
 | 
			
		||||
                    input_element_type = array_type.element().type_enum();
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
                TypeEnum::UInt(_) | TypeEnum::SInt(_)
 | 
			
		||||
                    if input_array_types
 | 
			
		||||
                        .first()
 | 
			
		||||
                        .map(|&v| single_type.is_array_type(v))
 | 
			
		||||
                        .unwrap_or(true) =>
 | 
			
		||||
                {
 | 
			
		||||
                    if let (Some(wire_rdata), Some(port_rdata)) = (wire_rdata, port_rdata) {
 | 
			
		||||
                        connect_read(output_stmts, wire_rdata, port_rdata);
 | 
			
		||||
                    }
 | 
			
		||||
                    if let (Some(wire_wdata), Some(port_wdata)) = (wire_wdata, port_wdata) {
 | 
			
		||||
                        connect_write(output_stmts, wire_wdata, port_wdata);
 | 
			
		||||
                    }
 | 
			
		||||
                    if let (Some(wire_wmask), Some(port_wmask)) = (wire_wmask, port_wmask) {
 | 
			
		||||
                        connect_write(output_stmts, wire_wmask, port_wmask);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                TypeEnum::UInt(_) | TypeEnum::SInt(_) => Self::connect_split_mem_port_arrays(
 | 
			
		||||
                    output_stmts,
 | 
			
		||||
                    &input_array_types,
 | 
			
		||||
                    0,
 | 
			
		||||
                    single_type.array_len(),
 | 
			
		||||
                    wire_rdata,
 | 
			
		||||
                    wire_wdata,
 | 
			
		||||
                    wire_wmask,
 | 
			
		||||
                    port_rdata.map(|e: Expr<DynCanonicalValue>| {
 | 
			
		||||
                        e.with_type::<Array<[DynCanonicalValue]>>()
 | 
			
		||||
                    }),
 | 
			
		||||
                    port_wdata.map(|e: Expr<DynCanonicalValue>| {
 | 
			
		||||
                        e.with_type::<Array<[DynCanonicalValue]>>()
 | 
			
		||||
                    }),
 | 
			
		||||
                    port_wmask.map(|e: Expr<DynCanonicalValue>| {
 | 
			
		||||
                        e.with_type::<Array<[DynCanonicalValue]>>()
 | 
			
		||||
                    }),
 | 
			
		||||
                    connect_read,
 | 
			
		||||
                    connect_write,
 | 
			
		||||
                    connect_write,
 | 
			
		||||
                ),
 | 
			
		||||
                TypeEnum::Clock(_)
 | 
			
		||||
                | TypeEnum::AsyncReset(_)
 | 
			
		||||
                | TypeEnum::SyncReset(_)
 | 
			
		||||
                | TypeEnum::Reset(_) => unreachable!("memory element type is a storable type"),
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    fn create_split_mem(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        input_mem: Mem<[DynCanonicalValue]>,
 | 
			
		||||
        output_stmts: &mut Vec<Stmt>,
 | 
			
		||||
        input_element_type: TypeEnum,
 | 
			
		||||
        single_type: SingleType,
 | 
			
		||||
        mem_name_path: &str,
 | 
			
		||||
        split_state: &SplitState<'_>,
 | 
			
		||||
    ) -> Mem<[DynCanonicalValue]> {
 | 
			
		||||
        let mem_name = self.name_id_gen.gen(Intern::intern_owned(format!(
 | 
			
		||||
            "{}{mem_name_path}",
 | 
			
		||||
            input_mem.scoped_name().1 .0
 | 
			
		||||
        )));
 | 
			
		||||
        let mem_name = ScopedNameId(input_mem.scoped_name().0, mem_name);
 | 
			
		||||
        let output_element_type = match single_type {
 | 
			
		||||
            SingleType::UInt(ty) => ty.canonical_dyn(),
 | 
			
		||||
            SingleType::SInt(ty) => ty.canonical_dyn(),
 | 
			
		||||
            SingleType::UIntArray(ty) => ty.canonical_dyn(),
 | 
			
		||||
            SingleType::SIntArray(ty) => ty.canonical_dyn(),
 | 
			
		||||
        };
 | 
			
		||||
        let output_array_type =
 | 
			
		||||
            ArrayType::new_slice(output_element_type, input_mem.array_type().len());
 | 
			
		||||
        let initial_value = split_state.initial_value.as_ref().map(|initial_value| {
 | 
			
		||||
            let mut bits = BitVec::with_capacity(output_array_type.bit_width());
 | 
			
		||||
            for element in initial_value.iter() {
 | 
			
		||||
                bits.extend_from_bitslice(element);
 | 
			
		||||
            }
 | 
			
		||||
            Intern::intern_owned(bits)
 | 
			
		||||
        });
 | 
			
		||||
        let ports = input_mem
 | 
			
		||||
            .ports()
 | 
			
		||||
            .into_iter()
 | 
			
		||||
            .map(|port| {
 | 
			
		||||
                MemPort::new_unchecked(
 | 
			
		||||
                    mem_name,
 | 
			
		||||
                    port.source_location(),
 | 
			
		||||
                    port.port_name(),
 | 
			
		||||
                    port.addr_type(),
 | 
			
		||||
                    output_element_type,
 | 
			
		||||
                )
 | 
			
		||||
                .intern_sized()
 | 
			
		||||
            })
 | 
			
		||||
            .collect();
 | 
			
		||||
        let output_mem = Mem::new_unchecked(
 | 
			
		||||
            mem_name,
 | 
			
		||||
            input_mem.source_location(),
 | 
			
		||||
            output_array_type,
 | 
			
		||||
            initial_value,
 | 
			
		||||
            ports,
 | 
			
		||||
            input_mem.read_latency(),
 | 
			
		||||
            input_mem.write_latency(),
 | 
			
		||||
            input_mem.read_under_write(),
 | 
			
		||||
            input_mem
 | 
			
		||||
                .port_annotations()
 | 
			
		||||
                .iter()
 | 
			
		||||
                .flat_map(|_v| -> Option<TargetedAnnotation> {
 | 
			
		||||
                    // TODO: map annotation target for memory port
 | 
			
		||||
                    None
 | 
			
		||||
                })
 | 
			
		||||
                .collect(),
 | 
			
		||||
            input_mem.mem_annotations(),
 | 
			
		||||
        );
 | 
			
		||||
        for (index, port) in ports.into_iter().enumerate() {
 | 
			
		||||
            let SplitState {
 | 
			
		||||
                wire_rdata,
 | 
			
		||||
                wire_wdata,
 | 
			
		||||
                wire_wmask,
 | 
			
		||||
                initial_value: _,
 | 
			
		||||
            } = split_state;
 | 
			
		||||
            let port_expr = port.to_expr();
 | 
			
		||||
            let port_rdata = port
 | 
			
		||||
                .port_kind()
 | 
			
		||||
                .rdata_name()
 | 
			
		||||
                .map(|name| port_expr.field(name));
 | 
			
		||||
            let port_wdata = port
 | 
			
		||||
                .port_kind()
 | 
			
		||||
                .wdata_name()
 | 
			
		||||
                .map(|name| port_expr.field(name));
 | 
			
		||||
            let port_wmask = port
 | 
			
		||||
                .port_kind()
 | 
			
		||||
                .wmask_name()
 | 
			
		||||
                .map(|name| port_expr.field(name));
 | 
			
		||||
            self.connect_split_mem_port(
 | 
			
		||||
                output_stmts,
 | 
			
		||||
                input_element_type,
 | 
			
		||||
                single_type,
 | 
			
		||||
                port.source_location(),
 | 
			
		||||
                wire_rdata[index],
 | 
			
		||||
                wire_wdata[index],
 | 
			
		||||
                wire_wmask[index],
 | 
			
		||||
                port_rdata,
 | 
			
		||||
                port_wdata,
 | 
			
		||||
                port_wmask,
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
        output_mem
 | 
			
		||||
    }
 | 
			
		||||
    fn process_mem(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        input_mem: Mem<[DynCanonicalValue]>,
 | 
			
		||||
        output_mems: &mut Vec<Mem<[DynCanonicalValue]>>,
 | 
			
		||||
        output_stmts: &mut Vec<Stmt>,
 | 
			
		||||
    ) {
 | 
			
		||||
        let element_type = input_mem.array_type().element().type_enum();
 | 
			
		||||
        let mut split = MemSplit::new(element_type);
 | 
			
		||||
        let mem_state = match split {
 | 
			
		||||
            MemSplit::Single {
 | 
			
		||||
                ref mut output_mem,
 | 
			
		||||
                element_type: _,
 | 
			
		||||
                unchanged_element_type: true,
 | 
			
		||||
            } => {
 | 
			
		||||
                // no change necessary
 | 
			
		||||
                *output_mem = Some(input_mem);
 | 
			
		||||
                output_mems.push(input_mem);
 | 
			
		||||
                MemState {
 | 
			
		||||
                    replacement_ports: input_mem
 | 
			
		||||
                        .ports()
 | 
			
		||||
                        .into_iter()
 | 
			
		||||
                        .map(ExprEnum::MemPort)
 | 
			
		||||
                        .collect(),
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            MemSplit::Single {
 | 
			
		||||
                unchanged_element_type: false,
 | 
			
		||||
                ..
 | 
			
		||||
            }
 | 
			
		||||
            | MemSplit::Bundle { .. }
 | 
			
		||||
            | MemSplit::Array { .. } => {
 | 
			
		||||
                let mut replacement_ports = Vec::with_capacity(input_mem.ports().len());
 | 
			
		||||
                let mut wire_port_rdata = Vec::with_capacity(input_mem.ports().len());
 | 
			
		||||
                let mut wire_port_wdata = Vec::with_capacity(input_mem.ports().len());
 | 
			
		||||
                let mut wire_port_wmask = Vec::with_capacity(input_mem.ports().len());
 | 
			
		||||
                for port in input_mem.ports() {
 | 
			
		||||
                    let port_ty = port.ty();
 | 
			
		||||
                    let NameId(mem_name, _) = input_mem.scoped_name().1;
 | 
			
		||||
                    let port_name = port.port_name();
 | 
			
		||||
                    let wire_name = self
 | 
			
		||||
                        .name_id_gen
 | 
			
		||||
                        .gen(Intern::intern_owned(format!("{mem_name}_{port_name}")));
 | 
			
		||||
                    let wire = Wire::new_unchecked(
 | 
			
		||||
                        ScopedNameId(input_mem.scoped_name().0, wire_name),
 | 
			
		||||
                        port.source_location(),
 | 
			
		||||
                        port_ty,
 | 
			
		||||
                    );
 | 
			
		||||
                    let wire_expr = wire.to_expr();
 | 
			
		||||
                    let canonical_wire = wire.to_dyn_canonical_wire();
 | 
			
		||||
                    output_stmts.push(
 | 
			
		||||
                        StmtWire {
 | 
			
		||||
                            annotations: Default::default(),
 | 
			
		||||
                            wire: canonical_wire.clone(),
 | 
			
		||||
                        }
 | 
			
		||||
                        .into(),
 | 
			
		||||
                    );
 | 
			
		||||
                    replacement_ports.push(ExprEnum::Wire(canonical_wire.intern_sized()));
 | 
			
		||||
                    wire_port_rdata.push(
 | 
			
		||||
                        port.port_kind()
 | 
			
		||||
                            .rdata_name()
 | 
			
		||||
                            .map(|name| wire_expr.field(name)),
 | 
			
		||||
                    );
 | 
			
		||||
                    wire_port_wdata.push(
 | 
			
		||||
                        port.port_kind()
 | 
			
		||||
                            .wdata_name()
 | 
			
		||||
                            .map(|name| wire_expr.field(name)),
 | 
			
		||||
                    );
 | 
			
		||||
                    wire_port_wmask.push(
 | 
			
		||||
                        port.port_kind()
 | 
			
		||||
                            .wmask_name()
 | 
			
		||||
                            .map(|name| wire_expr.field(name)),
 | 
			
		||||
                    );
 | 
			
		||||
                }
 | 
			
		||||
                let mem_state = MemState {
 | 
			
		||||
                    replacement_ports: replacement_ports.into_boxed_slice(),
 | 
			
		||||
                };
 | 
			
		||||
                SplitMemState {
 | 
			
		||||
                    module_state: self,
 | 
			
		||||
                    input_mem,
 | 
			
		||||
                    output_mems,
 | 
			
		||||
                    output_stmts,
 | 
			
		||||
                    element_type,
 | 
			
		||||
                    split: &mut split,
 | 
			
		||||
                    mem_name_path: &mut String::with_capacity(32),
 | 
			
		||||
                    split_state_stack: &mut SplitStateStack::new(
 | 
			
		||||
                        input_mem.ports().len(),
 | 
			
		||||
                        SplitState {
 | 
			
		||||
                            wire_rdata: wire_port_rdata.into_boxed_slice(),
 | 
			
		||||
                            wire_wdata: wire_port_wdata.into_boxed_slice(),
 | 
			
		||||
                            wire_wmask: wire_port_wmask.into_boxed_slice(),
 | 
			
		||||
                            initial_value: input_mem.initial_value().as_ref().map(
 | 
			
		||||
                                |initial_value| {
 | 
			
		||||
                                    if initial_value.len() == 0 {
 | 
			
		||||
                                        Box::new([])
 | 
			
		||||
                                    } else {
 | 
			
		||||
                                        Box::from_iter(initial_value.chunks(
 | 
			
		||||
                                            initial_value.len() / input_mem.array_type().len(),
 | 
			
		||||
                                        ))
 | 
			
		||||
                                    }
 | 
			
		||||
                                },
 | 
			
		||||
                            ),
 | 
			
		||||
                        },
 | 
			
		||||
                    ),
 | 
			
		||||
                    mem_state: &mem_state,
 | 
			
		||||
                }
 | 
			
		||||
                .split_mem();
 | 
			
		||||
                mem_state
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
        self.memories.insert(input_mem.scoped_name(), mem_state);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Default)]
 | 
			
		||||
struct State {
 | 
			
		||||
    modules: HashMap<Interned<Module<DynBundle>>, ModuleState>,
 | 
			
		||||
    current_module: Option<Interned<Module<DynBundle>>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl State {
 | 
			
		||||
    fn module_state(&mut self) -> &mut ModuleState {
 | 
			
		||||
        let current_module = self.current_module.unwrap();
 | 
			
		||||
        self.modules.get_mut(¤t_module).unwrap()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct PushedState<'a> {
 | 
			
		||||
    state: &'a mut State,
 | 
			
		||||
    old_module: Option<Interned<Module<DynBundle>>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> PushedState<'a> {
 | 
			
		||||
    fn push_module(state: &'a mut State, module: Interned<Module<DynBundle>>) -> Self {
 | 
			
		||||
        let old_module = state.current_module.replace(module);
 | 
			
		||||
        Self { state, old_module }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Drop for PushedState<'_> {
 | 
			
		||||
    fn drop(&mut self) {
 | 
			
		||||
        self.state.current_module = self.old_module.take();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Deref for PushedState<'_> {
 | 
			
		||||
    type Target = State;
 | 
			
		||||
    fn deref(&self) -> &Self::Target {
 | 
			
		||||
        self.state
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl DerefMut for PushedState<'_> {
 | 
			
		||||
    fn deref_mut(&mut self) -> &mut Self::Target {
 | 
			
		||||
        self.state
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Folder for State {
 | 
			
		||||
    type Error = Infallible;
 | 
			
		||||
 | 
			
		||||
    fn fold_module<T: BundleValue>(&mut self, v: Module<T>) -> Result<Module<T>, Self::Error>
 | 
			
		||||
    where
 | 
			
		||||
        T::Type: BundleType<Value = T>,
 | 
			
		||||
    {
 | 
			
		||||
        let module: Interned<_> = v.canonical().intern_sized();
 | 
			
		||||
        if let Some(module_state) = self.modules.get(&module) {
 | 
			
		||||
            return Ok(Module::from_canonical(
 | 
			
		||||
                *module_state
 | 
			
		||||
                    .output_module
 | 
			
		||||
                    .expect("modules can't be mutually recursive"),
 | 
			
		||||
            ));
 | 
			
		||||
        }
 | 
			
		||||
        self.modules.insert(
 | 
			
		||||
            module,
 | 
			
		||||
            ModuleState {
 | 
			
		||||
                output_module: None,
 | 
			
		||||
                name_id_gen: NameIdGen::for_module(*module),
 | 
			
		||||
                memories: HashMap::new(),
 | 
			
		||||
            },
 | 
			
		||||
        );
 | 
			
		||||
        let mut this = PushedState::push_module(self, module);
 | 
			
		||||
        let module = module.default_fold(&mut *this)?;
 | 
			
		||||
        this.module_state().output_module = Some(module);
 | 
			
		||||
        Ok(Module::from_canonical(*module))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn fold_mem<VA: ValueArrayOrSlice + ?Sized>(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        _v: Mem<VA>,
 | 
			
		||||
    ) -> Result<Mem<VA>, Self::Error> {
 | 
			
		||||
        unreachable!()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn fold_block(&mut self, v: Block) -> Result<Block, Self::Error> {
 | 
			
		||||
        let Block {
 | 
			
		||||
            memories: input_mems,
 | 
			
		||||
            stmts: input_stmts,
 | 
			
		||||
        } = v;
 | 
			
		||||
        let mut output_mems = vec![];
 | 
			
		||||
        let mut output_stmts = vec![];
 | 
			
		||||
        let module_state = self.module_state();
 | 
			
		||||
        for input_mem in input_mems {
 | 
			
		||||
            module_state.process_mem(input_mem, &mut output_mems, &mut output_stmts);
 | 
			
		||||
        }
 | 
			
		||||
        output_stmts.extend(
 | 
			
		||||
            input_stmts
 | 
			
		||||
                .into_iter()
 | 
			
		||||
                .map(|stmt| stmt.fold(self).unwrap_or_else(|v| match v {})),
 | 
			
		||||
        );
 | 
			
		||||
        Ok(Block {
 | 
			
		||||
            memories: Intern::intern_owned(output_mems),
 | 
			
		||||
            stmts: Intern::intern_owned(output_stmts),
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn fold_expr_enum(&mut self, v: ExprEnum) -> Result<ExprEnum, Self::Error> {
 | 
			
		||||
        if let ExprEnum::MemPort(mem_port) = v {
 | 
			
		||||
            Ok(self
 | 
			
		||||
                .module_state()
 | 
			
		||||
                .memories
 | 
			
		||||
                .get(&mem_port.mem_name())
 | 
			
		||||
                .expect("all uses of a memory must come after the memory is declared")
 | 
			
		||||
                .replacement_ports[mem_port.port_index()])
 | 
			
		||||
        } else {
 | 
			
		||||
            v.default_fold(self)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn fold_mem_port<T: PortType>(&mut self, _v: MemPort<T>) -> Result<MemPort<T>, Self::Error> {
 | 
			
		||||
        unreachable!()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn simplify_memories(module: Interned<Module<DynBundle>>) -> Interned<Module<DynBundle>> {
 | 
			
		||||
    module
 | 
			
		||||
        .fold(&mut State::default())
 | 
			
		||||
        .unwrap_or_else(|v| match v {})
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										462
									
								
								crates/fayalite/src/module/transform/visit.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										462
									
								
								crates/fayalite/src/module/transform/visit.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,462 @@
 | 
			
		|||
// SPDX-License-Identifier: LGPL-3.0-or-later
 | 
			
		||||
// See Notices.txt for copyright information
 | 
			
		||||
#![allow(clippy::multiple_bound_locations)]
 | 
			
		||||
use crate::{
 | 
			
		||||
    annotations::{Annotation, CustomFirrtlAnnotation, TargetedAnnotation},
 | 
			
		||||
    array::{Array, ArrayType, ArrayTypeTrait, ValueArrayOrSlice},
 | 
			
		||||
    bundle::{BundleType, BundleValue, DynBundle, DynBundleType, FieldType},
 | 
			
		||||
    clock::{Clock, ClockType},
 | 
			
		||||
    enum_::{DynEnum, DynEnumType, EnumType, EnumValue, VariantType},
 | 
			
		||||
    expr::{
 | 
			
		||||
        ops, Expr, ExprEnum, Literal, Target, TargetBase, TargetChild, TargetPathArrayElement,
 | 
			
		||||
        TargetPathBundleField, TargetPathDynArrayElement, TargetPathElement, ToExpr,
 | 
			
		||||
    },
 | 
			
		||||
    int::{DynInt, DynIntType, FixedOrDynIntType, IntType, IntTypeTrait},
 | 
			
		||||
    intern::{Intern, Interned},
 | 
			
		||||
    memory::{Mem, MemPort, PortKind, PortName, PortType, ReadUnderWrite},
 | 
			
		||||
    module::{
 | 
			
		||||
        AnnotatedModuleIO, Block, BlockId, ExternModuleBody, ExternModuleParameter,
 | 
			
		||||
        ExternModuleParameterValue, Instance, Module, ModuleBody, ModuleIO, NameId,
 | 
			
		||||
        NormalModuleBody, ScopedNameId, Stmt, StmtConnect, StmtDeclaration, StmtIf, StmtInstance,
 | 
			
		||||
        StmtMatch, StmtReg, StmtWire,
 | 
			
		||||
    },
 | 
			
		||||
    reg::Reg,
 | 
			
		||||
    reset::{AsyncReset, AsyncResetType, Reset, ResetType, SyncReset, SyncResetType},
 | 
			
		||||
    source_location::SourceLocation,
 | 
			
		||||
    ty::{DynCanonicalType, DynCanonicalValue, DynType, Type, TypeEnum, Value, ValueEnum},
 | 
			
		||||
    util::{ConstBool, GenericConstBool},
 | 
			
		||||
    wire::Wire,
 | 
			
		||||
};
 | 
			
		||||
use num_bigint::{BigInt, BigUint};
 | 
			
		||||
use std::{rc::Rc, sync::Arc};
 | 
			
		||||
 | 
			
		||||
pub trait Fold<State: ?Sized + Folder>: Sized {
 | 
			
		||||
    fn fold(self, state: &mut State) -> Result<Self, State::Error>;
 | 
			
		||||
    fn default_fold(self, state: &mut State) -> Result<Self, State::Error>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub trait Visit<State: ?Sized + Visitor> {
 | 
			
		||||
    fn visit(&self, state: &mut State) -> Result<(), State::Error>;
 | 
			
		||||
    fn default_visit(&self, state: &mut State) -> Result<(), State::Error>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
macro_rules! impl_visit_fold {
 | 
			
		||||
    (
 | 
			
		||||
        impl<$T:ident, $State:ident $(, const $Const:ident: $const_ty:ty)*> _ for $ty:ty $(where ($($where_tt:tt)*))? {
 | 
			
		||||
            fn fold($($fold_args:tt)*) -> $fold_ret_ty:ty $fold_block:block
 | 
			
		||||
            fn default_fold($($default_fold_args:tt)*) -> $default_fold_ret_ty:ty $default_fold_block:block
 | 
			
		||||
            fn visit($($visit_args:tt)*) -> $visit_ret_ty:ty $visit_block:block
 | 
			
		||||
            fn default_visit($($default_visit_args:tt)*) -> $default_visit_ret_ty:ty $default_visit_block:block
 | 
			
		||||
        }
 | 
			
		||||
    ) => {
 | 
			
		||||
        impl<$T: Fold<$State>, $State: ?Sized + Folder $(, const $Const: $const_ty)*> Fold<$State> for $ty
 | 
			
		||||
        $(where $($where_tt)*)?
 | 
			
		||||
        {
 | 
			
		||||
            fn fold($($fold_args)*) -> $fold_ret_ty $fold_block
 | 
			
		||||
            fn default_fold($($default_fold_args)*) -> $default_fold_ret_ty $default_fold_block
 | 
			
		||||
        }
 | 
			
		||||
        impl<$T: Visit<$State>, $State: ?Sized + Visitor $(, const $Const: $const_ty)*> Visit<$State> for $ty
 | 
			
		||||
        $(where $($where_tt)*)?
 | 
			
		||||
        {
 | 
			
		||||
            fn visit($($visit_args)*) -> $visit_ret_ty $visit_block
 | 
			
		||||
            fn default_visit($($default_visit_args)*) -> $default_visit_ret_ty $default_visit_block
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
macro_rules! no_op_visit_fold {
 | 
			
		||||
    ($ty:ty) => {
 | 
			
		||||
        impl<State: ?Sized + Folder> Fold<State> for $ty {
 | 
			
		||||
            fn fold(self, state: &mut State) -> Result<Self, State::Error> {
 | 
			
		||||
                let _ = state;
 | 
			
		||||
                Ok(self)
 | 
			
		||||
            }
 | 
			
		||||
            fn default_fold(self, state: &mut State) -> Result<Self, State::Error> {
 | 
			
		||||
                let _ = state;
 | 
			
		||||
                Ok(self)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        impl<State: ?Sized + Visitor> Visit<State> for $ty {
 | 
			
		||||
            fn visit(&self, state: &mut State) -> Result<(), <State>::Error> {
 | 
			
		||||
                let _ = state;
 | 
			
		||||
                Ok(())
 | 
			
		||||
            }
 | 
			
		||||
            fn default_visit(&self, state: &mut State) -> Result<(), <State>::Error> {
 | 
			
		||||
                let _ = state;
 | 
			
		||||
                Ok(())
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
no_op_visit_fold!(());
 | 
			
		||||
no_op_visit_fold!(char);
 | 
			
		||||
no_op_visit_fold!(u8);
 | 
			
		||||
no_op_visit_fold!(u16);
 | 
			
		||||
no_op_visit_fold!(u32);
 | 
			
		||||
no_op_visit_fold!(u64);
 | 
			
		||||
no_op_visit_fold!(u128);
 | 
			
		||||
no_op_visit_fold!(usize);
 | 
			
		||||
no_op_visit_fold!(i8);
 | 
			
		||||
no_op_visit_fold!(i16);
 | 
			
		||||
no_op_visit_fold!(i32);
 | 
			
		||||
no_op_visit_fold!(i64);
 | 
			
		||||
no_op_visit_fold!(i128);
 | 
			
		||||
no_op_visit_fold!(isize);
 | 
			
		||||
no_op_visit_fold!(std::num::NonZeroU8);
 | 
			
		||||
no_op_visit_fold!(std::num::NonZeroU16);
 | 
			
		||||
no_op_visit_fold!(std::num::NonZeroU32);
 | 
			
		||||
no_op_visit_fold!(std::num::NonZeroU64);
 | 
			
		||||
no_op_visit_fold!(std::num::NonZeroU128);
 | 
			
		||||
no_op_visit_fold!(std::num::NonZeroUsize);
 | 
			
		||||
no_op_visit_fold!(std::num::NonZeroI8);
 | 
			
		||||
no_op_visit_fold!(std::num::NonZeroI16);
 | 
			
		||||
no_op_visit_fold!(std::num::NonZeroI32);
 | 
			
		||||
no_op_visit_fold!(std::num::NonZeroI64);
 | 
			
		||||
no_op_visit_fold!(std::num::NonZeroI128);
 | 
			
		||||
no_op_visit_fold!(std::num::NonZeroIsize);
 | 
			
		||||
no_op_visit_fold!(bool);
 | 
			
		||||
no_op_visit_fold!(String);
 | 
			
		||||
no_op_visit_fold!(Interned<str>);
 | 
			
		||||
no_op_visit_fold!(Box<str>);
 | 
			
		||||
no_op_visit_fold!(Arc<str>);
 | 
			
		||||
no_op_visit_fold!(Rc<str>);
 | 
			
		||||
no_op_visit_fold!(BigInt);
 | 
			
		||||
no_op_visit_fold!(BigUint);
 | 
			
		||||
 | 
			
		||||
impl_visit_fold! {
 | 
			
		||||
    impl<T, State> _ for Box<T> {
 | 
			
		||||
        fn fold(mut self, state: &mut State) -> Result<Self, State::Error> {
 | 
			
		||||
            *self = T::fold(*self, state)?;
 | 
			
		||||
            Ok(self)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn default_fold(mut self, state: &mut State) -> Result<Self, State::Error> {
 | 
			
		||||
            *self = T::default_fold(*self, state)?;
 | 
			
		||||
            Ok(self)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn visit(&self, state: &mut State) -> Result<(), <State>::Error> {
 | 
			
		||||
            T::visit(self, state)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn default_visit(&self, state: &mut State) -> Result<(), <State>::Error> {
 | 
			
		||||
            T::default_visit(self, state)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl_visit_fold! {
 | 
			
		||||
    impl<T, State> _ for Arc<T>
 | 
			
		||||
    where (
 | 
			
		||||
        T: Clone,
 | 
			
		||||
    )
 | 
			
		||||
    {
 | 
			
		||||
        fn fold(mut self, state: &mut State) -> Result<Self, State::Error> {
 | 
			
		||||
            if let Some(v) = Arc::get_mut(&mut self) {
 | 
			
		||||
                *v = T::fold(v.clone(), state)?;
 | 
			
		||||
                Ok(self)
 | 
			
		||||
            } else {
 | 
			
		||||
                T::fold(T::clone(&*self), state).map(Arc::new)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn default_fold(mut self, state: &mut State) -> Result<Self, State::Error> {
 | 
			
		||||
            if let Some(v) = Arc::get_mut(&mut self) {
 | 
			
		||||
                *v = T::default_fold(v.clone(), state)?;
 | 
			
		||||
                Ok(self)
 | 
			
		||||
            } else {
 | 
			
		||||
                T::default_fold(T::clone(&*self), state).map(Arc::new)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn visit(&self, state: &mut State) -> Result<(), <State>::Error> {
 | 
			
		||||
            T::visit(self, state)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn default_visit(&self, state: &mut State) -> Result<(), <State>::Error> {
 | 
			
		||||
            T::default_visit(self, state)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl_visit_fold! {
 | 
			
		||||
    impl<T, State> _ for Rc<T>
 | 
			
		||||
    where (
 | 
			
		||||
        T: Clone,
 | 
			
		||||
    )
 | 
			
		||||
    {
 | 
			
		||||
        fn fold(mut self, state: &mut State) -> Result<Self, State::Error> {
 | 
			
		||||
            if let Some(v) = Rc::get_mut(&mut self) {
 | 
			
		||||
                *v = T::fold(v.clone(), state)?;
 | 
			
		||||
                Ok(self)
 | 
			
		||||
            } else {
 | 
			
		||||
                T::fold(T::clone(&*self), state).map(Rc::new)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn default_fold(mut self, state: &mut State) -> Result<Self, State::Error> {
 | 
			
		||||
            if let Some(v) = Rc::get_mut(&mut self) {
 | 
			
		||||
                *v = T::default_fold(v.clone(), state)?;
 | 
			
		||||
                Ok(self)
 | 
			
		||||
            } else {
 | 
			
		||||
                T::default_fold(T::clone(&*self), state).map(Rc::new)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn visit(&self, state: &mut State) -> Result<(), <State>::Error> {
 | 
			
		||||
            T::visit(self, state)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn default_visit(&self, state: &mut State) -> Result<(), <State>::Error> {
 | 
			
		||||
            T::default_visit(self, state)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl_visit_fold! {
 | 
			
		||||
    impl<T, State> _ for Arc<[T]>
 | 
			
		||||
    where (
 | 
			
		||||
        T: Clone,
 | 
			
		||||
    ) {
 | 
			
		||||
        fn fold(mut self, state: &mut State) -> Result<Self, State::Error> {
 | 
			
		||||
            if let Some(v) = Arc::get_mut(&mut self) {
 | 
			
		||||
                v.iter_mut().try_for_each(|v| {
 | 
			
		||||
                    *v = v.clone().fold(state)?;
 | 
			
		||||
                    Ok(())
 | 
			
		||||
                })?;
 | 
			
		||||
                Ok(self)
 | 
			
		||||
            } else {
 | 
			
		||||
                FromIterator::from_iter(self.iter().map(|v| v.clone().fold(state)))
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn default_fold(mut self, state: &mut State) -> Result<Self, State::Error> {
 | 
			
		||||
            if let Some(v) = Arc::get_mut(&mut self) {
 | 
			
		||||
                v.iter_mut().try_for_each(|v| {
 | 
			
		||||
                    *v = v.clone().default_fold(state)?;
 | 
			
		||||
                    Ok(())
 | 
			
		||||
                })?;
 | 
			
		||||
                Ok(self)
 | 
			
		||||
            } else {
 | 
			
		||||
                FromIterator::from_iter(self.iter().map(|v| v.clone().default_fold(state)))
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn visit(&self, state: &mut State) -> Result<(), <State>::Error> {
 | 
			
		||||
            self.iter().try_for_each(|v| T::visit(v, state))
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn default_visit(&self, state: &mut State) -> Result<(), <State>::Error> {
 | 
			
		||||
            self.iter().try_for_each(|v| T::default_visit(v, state))
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl_visit_fold! {
 | 
			
		||||
    impl<T, State> _ for Rc<[T]>
 | 
			
		||||
    where (
 | 
			
		||||
        T: Clone,
 | 
			
		||||
    ) {
 | 
			
		||||
        fn fold(mut self, state: &mut State) -> Result<Self, State::Error> {
 | 
			
		||||
            if let Some(v) = Rc::get_mut(&mut self) {
 | 
			
		||||
                v.iter_mut().try_for_each(|v| {
 | 
			
		||||
                    *v = v.clone().fold(state)?;
 | 
			
		||||
                    Ok(())
 | 
			
		||||
                })?;
 | 
			
		||||
                Ok(self)
 | 
			
		||||
            } else {
 | 
			
		||||
                FromIterator::from_iter(self.iter().map(|v| v.clone().fold(state)))
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn default_fold(mut self, state: &mut State) -> Result<Self, State::Error> {
 | 
			
		||||
            if let Some(v) = Rc::get_mut(&mut self) {
 | 
			
		||||
                v.iter_mut().try_for_each(|v| {
 | 
			
		||||
                    *v = v.clone().default_fold(state)?;
 | 
			
		||||
                    Ok(())
 | 
			
		||||
                })?;
 | 
			
		||||
                Ok(self)
 | 
			
		||||
            } else {
 | 
			
		||||
                FromIterator::from_iter(self.iter().map(|v| v.clone().default_fold(state)))
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn visit(&self, state: &mut State) -> Result<(), <State>::Error> {
 | 
			
		||||
            self.iter().try_for_each(|v| T::visit(v, state))
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn default_visit(&self, state: &mut State) -> Result<(), <State>::Error> {
 | 
			
		||||
            self.iter().try_for_each(|v| T::default_visit(v, state))
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl_visit_fold! {
 | 
			
		||||
    impl<T, State> _ for Interned<T>
 | 
			
		||||
    where (
 | 
			
		||||
        T: Clone + Intern
 | 
			
		||||
    )
 | 
			
		||||
    {
 | 
			
		||||
        fn fold(self, state: &mut State) -> Result<Self, State::Error> {
 | 
			
		||||
            T::fold(T::clone(&self), state).map(Intern::intern_sized)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn default_fold(self, state: &mut State) -> Result<Self, State::Error> {
 | 
			
		||||
            T::default_fold(T::clone(&self), state).map(Intern::intern_sized)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn visit(&self, state: &mut State) -> Result<(), <State>::Error> {
 | 
			
		||||
            T::visit(self, state)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn default_visit(&self, state: &mut State) -> Result<(), <State>::Error> {
 | 
			
		||||
            T::default_visit(self, state)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl_visit_fold! {
 | 
			
		||||
    impl<T, State> _ for Interned<[T]>
 | 
			
		||||
    where (
 | 
			
		||||
        T: Clone + 'static + Send + Sync,
 | 
			
		||||
        [T]: Intern,
 | 
			
		||||
    )
 | 
			
		||||
    {
 | 
			
		||||
        fn fold(self, state: &mut State) -> Result<Self, State::Error> {
 | 
			
		||||
            FromIterator::from_iter(self.into_iter().map(|v| v.fold(state)))
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn default_fold(self, state: &mut State) -> Result<Self, State::Error> {
 | 
			
		||||
            FromIterator::from_iter(self.into_iter().map(|v| v.default_fold(state)))
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn visit(&self, state: &mut State) -> Result<(), <State>::Error> {
 | 
			
		||||
            self.iter().try_for_each(|v| T::visit(v, state))
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn default_visit(&self, state: &mut State) -> Result<(), <State>::Error> {
 | 
			
		||||
            self.iter().try_for_each(|v| T::default_visit(v, state))
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl_visit_fold! {
 | 
			
		||||
    impl<T, State> _ for Box<[T]> {
 | 
			
		||||
        fn fold(self, state: &mut State) -> Result<Self, State::Error> {
 | 
			
		||||
            FromIterator::from_iter(self.into_vec().into_iter().map(|v| v.fold(state)))
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn default_fold(self, state: &mut State) -> Result<Self, State::Error> {
 | 
			
		||||
            FromIterator::from_iter(self.into_vec().into_iter().map(|v| v.default_fold(state)))
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn visit(&self, state: &mut State) -> Result<(), <State>::Error> {
 | 
			
		||||
            self.iter().try_for_each(|v| T::visit(v, state))
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn default_visit(&self, state: &mut State) -> Result<(), <State>::Error> {
 | 
			
		||||
            self.iter().try_for_each(|v| T::default_visit(v, state))
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl_visit_fold! {
 | 
			
		||||
    impl<T, State, const N: usize> _ for [T; N] {
 | 
			
		||||
        fn fold(self, state: &mut State) -> Result<Self, State::Error> {
 | 
			
		||||
            let mut retval = std::array::from_fn(|_| None);
 | 
			
		||||
            for (retval, v) in retval.iter_mut().zip(self) {
 | 
			
		||||
                *retval = Some(v.fold(state)?);
 | 
			
		||||
            }
 | 
			
		||||
            Ok(retval.map(Option::unwrap))
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn default_fold(self, state: &mut State) -> Result<Self, State::Error> {
 | 
			
		||||
            let mut retval = std::array::from_fn(|_| None);
 | 
			
		||||
            for (retval, v) in retval.iter_mut().zip(self) {
 | 
			
		||||
                *retval = Some(v.default_fold(state)?);
 | 
			
		||||
            }
 | 
			
		||||
            Ok(retval.map(Option::unwrap))
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn visit(&self, state: &mut State) -> Result<(), <State>::Error> {
 | 
			
		||||
            self.iter().try_for_each(|v| T::visit(v, state))
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn default_visit(&self, state: &mut State) -> Result<(), <State>::Error> {
 | 
			
		||||
            self.iter().try_for_each(|v| T::default_visit(v, state))
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl_visit_fold! {
 | 
			
		||||
    impl<T, State> _ for Vec<T> {
 | 
			
		||||
        fn fold(self, state: &mut State) -> Result<Self, State::Error> {
 | 
			
		||||
            FromIterator::from_iter(self.into_iter().map(|v| v.fold(state)))
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn default_fold(self, state: &mut State) -> Result<Self, State::Error> {
 | 
			
		||||
            FromIterator::from_iter(self.into_iter().map(|v| v.default_fold(state)))
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn visit(&self, state: &mut State) -> Result<(), <State>::Error> {
 | 
			
		||||
            self.iter().try_for_each(|v| T::visit(v, state))
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn default_visit(&self, state: &mut State) -> Result<(), <State>::Error> {
 | 
			
		||||
            self.iter().try_for_each(|v| T::default_visit(v, state))
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl_visit_fold! {
 | 
			
		||||
    impl<T, State> _ for Option<T> {
 | 
			
		||||
        fn fold(self, state: &mut State) -> Result<Self, State::Error> {
 | 
			
		||||
            self.map(|v| v.fold(state)).transpose()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn default_fold(self, state: &mut State) -> Result<Self, State::Error> {
 | 
			
		||||
            self.map(|v| v.default_fold(state)).transpose()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn visit(&self, state: &mut State) -> Result<(), State::Error> {
 | 
			
		||||
            let Some(v) = self else {
 | 
			
		||||
                return Ok(());
 | 
			
		||||
            };
 | 
			
		||||
            T::visit(v, state)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn default_visit(&self, state: &mut State) -> Result<(), State::Error> {
 | 
			
		||||
            let Some(v) = self else {
 | 
			
		||||
                return Ok(());
 | 
			
		||||
            };
 | 
			
		||||
            T::default_visit(v, state)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: ?Sized + Visit<State>, State: ?Sized + Visitor> Visit<State> for &'_ T {
 | 
			
		||||
    fn visit(&self, state: &mut State) -> Result<(), <State>::Error> {
 | 
			
		||||
        T::visit(self, state)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn default_visit(&self, state: &mut State) -> Result<(), <State>::Error> {
 | 
			
		||||
        T::default_visit(self, state)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: ?Sized + Visit<State>, State: ?Sized + Visitor> Visit<State> for &'_ mut T {
 | 
			
		||||
    fn visit(&self, state: &mut State) -> Result<(), <State>::Error> {
 | 
			
		||||
        T::visit(self, state)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn default_visit(&self, state: &mut State) -> Result<(), <State>::Error> {
 | 
			
		||||
        T::default_visit(self, state)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type InternedDynType = Interned<dyn DynType>;
 | 
			
		||||
type InternedDynCanonicalType = Interned<dyn DynCanonicalType>;
 | 
			
		||||
 | 
			
		||||
include!(concat!(env!("OUT_DIR"), "/visit.rs"));
 | 
			
		||||
							
								
								
									
										151
									
								
								crates/fayalite/src/reg.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								crates/fayalite/src/reg.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,151 @@
 | 
			
		|||
// SPDX-License-Identifier: LGPL-3.0-or-later
 | 
			
		||||
// See Notices.txt for copyright information
 | 
			
		||||
use crate::{
 | 
			
		||||
    clock::ClockDomain,
 | 
			
		||||
    expr::{Expr, ExprTrait, Flow, ToExpr},
 | 
			
		||||
    intern::Interned,
 | 
			
		||||
    module::{NameId, ScopedNameId},
 | 
			
		||||
    source_location::SourceLocation,
 | 
			
		||||
    ty::{DynCanonicalType, DynType, Type},
 | 
			
		||||
};
 | 
			
		||||
use std::fmt;
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Eq, PartialEq, Hash)]
 | 
			
		||||
pub struct Reg<T: Type> {
 | 
			
		||||
    name: ScopedNameId,
 | 
			
		||||
    source_location: SourceLocation,
 | 
			
		||||
    ty: T,
 | 
			
		||||
    clock_domain: Expr<ClockDomain>,
 | 
			
		||||
    init: Option<Expr<T::Value>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: Type + fmt::Debug> fmt::Debug for Reg<T> {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        let Self {
 | 
			
		||||
            name,
 | 
			
		||||
            source_location: _,
 | 
			
		||||
            ty,
 | 
			
		||||
            clock_domain,
 | 
			
		||||
            init,
 | 
			
		||||
        } = self;
 | 
			
		||||
        f.debug_struct("Reg")
 | 
			
		||||
            .field("name", name)
 | 
			
		||||
            .field("ty", ty)
 | 
			
		||||
            .field("clock_domain", clock_domain)
 | 
			
		||||
            .field("init", init)
 | 
			
		||||
            .finish_non_exhaustive()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: Type> ToExpr for Reg<T> {
 | 
			
		||||
    type Type = T;
 | 
			
		||||
 | 
			
		||||
    fn ty(&self) -> Self::Type {
 | 
			
		||||
        self.ty.clone()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
 | 
			
		||||
        Expr::new_unchecked(self.expr_enum())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: Type> Reg<T> {
 | 
			
		||||
    pub fn canonical(&self) -> Reg<T::CanonicalType> {
 | 
			
		||||
        let Self {
 | 
			
		||||
            name,
 | 
			
		||||
            source_location,
 | 
			
		||||
            ref ty,
 | 
			
		||||
            clock_domain,
 | 
			
		||||
            init,
 | 
			
		||||
        } = *self;
 | 
			
		||||
        Reg {
 | 
			
		||||
            name,
 | 
			
		||||
            source_location,
 | 
			
		||||
            ty: ty.canonical(),
 | 
			
		||||
            clock_domain,
 | 
			
		||||
            init: init.map(Expr::canonical),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn to_dyn_reg(&self) -> Reg<Interned<dyn DynType>> {
 | 
			
		||||
        let Self {
 | 
			
		||||
            name,
 | 
			
		||||
            source_location,
 | 
			
		||||
            ref ty,
 | 
			
		||||
            clock_domain,
 | 
			
		||||
            init,
 | 
			
		||||
        } = *self;
 | 
			
		||||
        Reg {
 | 
			
		||||
            name,
 | 
			
		||||
            source_location,
 | 
			
		||||
            ty: ty.to_dyn(),
 | 
			
		||||
            clock_domain,
 | 
			
		||||
            init: init.map(Expr::to_dyn),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn to_dyn_canonical_reg(&self) -> Reg<Interned<dyn DynCanonicalType>> {
 | 
			
		||||
        let Self {
 | 
			
		||||
            name,
 | 
			
		||||
            source_location,
 | 
			
		||||
            ref ty,
 | 
			
		||||
            clock_domain,
 | 
			
		||||
            init,
 | 
			
		||||
        } = *self;
 | 
			
		||||
        Reg {
 | 
			
		||||
            name,
 | 
			
		||||
            source_location,
 | 
			
		||||
            ty: ty.canonical_dyn(),
 | 
			
		||||
            clock_domain,
 | 
			
		||||
            init: init.map(Expr::to_canonical_dyn),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    #[track_caller]
 | 
			
		||||
    pub fn new_unchecked(
 | 
			
		||||
        scoped_name: ScopedNameId,
 | 
			
		||||
        source_location: SourceLocation,
 | 
			
		||||
        ty: T,
 | 
			
		||||
        clock_domain: Expr<ClockDomain>,
 | 
			
		||||
        init: Option<Expr<T::Value>>,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        assert!(ty.is_storable(), "register type must be a storable type");
 | 
			
		||||
        if let Some(init) = init {
 | 
			
		||||
            assert_eq!(ty, init.ty(), "register's type must match init type");
 | 
			
		||||
        }
 | 
			
		||||
        Self {
 | 
			
		||||
            name: scoped_name,
 | 
			
		||||
            source_location,
 | 
			
		||||
            ty,
 | 
			
		||||
            clock_domain,
 | 
			
		||||
            init,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn source_location(&self) -> SourceLocation {
 | 
			
		||||
        self.source_location
 | 
			
		||||
    }
 | 
			
		||||
    pub fn containing_module_name(&self) -> Interned<str> {
 | 
			
		||||
        self.containing_module_name_id().0
 | 
			
		||||
    }
 | 
			
		||||
    pub fn containing_module_name_id(&self) -> NameId {
 | 
			
		||||
        self.name.0
 | 
			
		||||
    }
 | 
			
		||||
    pub fn name(&self) -> Interned<str> {
 | 
			
		||||
        self.name_id().0
 | 
			
		||||
    }
 | 
			
		||||
    pub fn name_id(&self) -> NameId {
 | 
			
		||||
        self.name.1
 | 
			
		||||
    }
 | 
			
		||||
    pub fn scoped_name(&self) -> ScopedNameId {
 | 
			
		||||
        self.name
 | 
			
		||||
    }
 | 
			
		||||
    pub fn clock_domain(&self) -> Expr<ClockDomain> {
 | 
			
		||||
        self.clock_domain
 | 
			
		||||
    }
 | 
			
		||||
    pub fn init(&self) -> Option<Expr<T::Value>> {
 | 
			
		||||
        self.init
 | 
			
		||||
    }
 | 
			
		||||
    pub fn flow(&self) -> Flow {
 | 
			
		||||
        Flow::Duplex
 | 
			
		||||
    }
 | 
			
		||||
    pub fn must_connect_to(&self) -> bool {
 | 
			
		||||
        false
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										359
									
								
								crates/fayalite/src/reset.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										359
									
								
								crates/fayalite/src/reset.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,359 @@
 | 
			
		|||
// SPDX-License-Identifier: LGPL-3.0-or-later
 | 
			
		||||
// See Notices.txt for copyright information
 | 
			
		||||
use crate::{
 | 
			
		||||
    expr::{Expr, ToExpr},
 | 
			
		||||
    int::{UInt, UIntType},
 | 
			
		||||
    intern::Interned,
 | 
			
		||||
    source_location::SourceLocation,
 | 
			
		||||
    ty::{
 | 
			
		||||
        impl_match_values_as_self, CanonicalType, CanonicalTypeKind, CanonicalValue, Connect,
 | 
			
		||||
        DynCanonicalType, FixedType, Type, TypeEnum, Value, ValueEnum,
 | 
			
		||||
    },
 | 
			
		||||
    util::interned_bit,
 | 
			
		||||
};
 | 
			
		||||
use bitvec::slice::BitSlice;
 | 
			
		||||
 | 
			
		||||
pub trait ResetTypeTrait: CanonicalType + FixedType {}
 | 
			
		||||
 | 
			
		||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)]
 | 
			
		||||
pub struct AsyncResetType;
 | 
			
		||||
 | 
			
		||||
impl AsyncResetType {
 | 
			
		||||
    pub const fn new() -> Self {
 | 
			
		||||
        Self
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Type for AsyncResetType {
 | 
			
		||||
    type Value = AsyncReset;
 | 
			
		||||
    type CanonicalType = AsyncResetType;
 | 
			
		||||
    type CanonicalValue = AsyncReset;
 | 
			
		||||
    type MaskType = UIntType<1>;
 | 
			
		||||
    type MaskValue = UInt<1>;
 | 
			
		||||
 | 
			
		||||
    impl_match_values_as_self!();
 | 
			
		||||
 | 
			
		||||
    fn mask_type(&self) -> Self::MaskType {
 | 
			
		||||
        UIntType::new()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn canonical(&self) -> Self::CanonicalType {
 | 
			
		||||
        *self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn source_location(&self) -> SourceLocation {
 | 
			
		||||
        SourceLocation::builtin()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn type_enum(&self) -> TypeEnum {
 | 
			
		||||
        TypeEnum::AsyncReset(*self)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn from_canonical_type(t: Self::CanonicalType) -> Self {
 | 
			
		||||
        t
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> {
 | 
			
		||||
        Some(this)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Connect<Self> for AsyncResetType {}
 | 
			
		||||
 | 
			
		||||
impl CanonicalType for AsyncResetType {
 | 
			
		||||
    const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::AsyncReset;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl FixedType for AsyncResetType {
 | 
			
		||||
    fn fixed_type() -> Self {
 | 
			
		||||
        Self
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ResetTypeTrait for AsyncResetType {}
 | 
			
		||||
 | 
			
		||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
 | 
			
		||||
pub struct AsyncReset(pub bool);
 | 
			
		||||
 | 
			
		||||
impl ToExpr for AsyncReset {
 | 
			
		||||
    type Type = AsyncResetType;
 | 
			
		||||
 | 
			
		||||
    fn ty(&self) -> Self::Type {
 | 
			
		||||
        AsyncResetType
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn to_expr(&self) -> Expr<Self> {
 | 
			
		||||
        Expr::from_value(self)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Value for AsyncReset {
 | 
			
		||||
    fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue {
 | 
			
		||||
        *self
 | 
			
		||||
    }
 | 
			
		||||
    fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
 | 
			
		||||
        interned_bit(this.0)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl CanonicalValue for AsyncReset {
 | 
			
		||||
    fn value_enum_impl(this: &Self) -> ValueEnum {
 | 
			
		||||
        ValueEnum::AsyncReset(*this)
 | 
			
		||||
    }
 | 
			
		||||
    fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
 | 
			
		||||
        interned_bit(this.0)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)]
 | 
			
		||||
pub struct SyncResetType;
 | 
			
		||||
 | 
			
		||||
impl SyncResetType {
 | 
			
		||||
    pub const fn new() -> Self {
 | 
			
		||||
        Self
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Type for SyncResetType {
 | 
			
		||||
    type CanonicalType = SyncResetType;
 | 
			
		||||
    type Value = SyncReset;
 | 
			
		||||
    type CanonicalValue = SyncReset;
 | 
			
		||||
    type MaskType = UIntType<1>;
 | 
			
		||||
    type MaskValue = UInt<1>;
 | 
			
		||||
 | 
			
		||||
    impl_match_values_as_self!();
 | 
			
		||||
 | 
			
		||||
    fn mask_type(&self) -> Self::MaskType {
 | 
			
		||||
        UIntType::new()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn canonical(&self) -> Self::CanonicalType {
 | 
			
		||||
        *self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn source_location(&self) -> SourceLocation {
 | 
			
		||||
        SourceLocation::builtin()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn type_enum(&self) -> TypeEnum {
 | 
			
		||||
        TypeEnum::SyncReset(*self)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn from_canonical_type(t: Self::CanonicalType) -> Self {
 | 
			
		||||
        t
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> {
 | 
			
		||||
        Some(this)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Connect<Self> for SyncResetType {}
 | 
			
		||||
 | 
			
		||||
impl CanonicalType for SyncResetType {
 | 
			
		||||
    const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::SyncReset;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl FixedType for SyncResetType {
 | 
			
		||||
    fn fixed_type() -> Self {
 | 
			
		||||
        Self
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ResetTypeTrait for SyncResetType {}
 | 
			
		||||
 | 
			
		||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)]
 | 
			
		||||
pub struct SyncReset(pub bool);
 | 
			
		||||
 | 
			
		||||
impl ToExpr for SyncReset {
 | 
			
		||||
    type Type = SyncResetType;
 | 
			
		||||
 | 
			
		||||
    fn ty(&self) -> Self::Type {
 | 
			
		||||
        SyncResetType
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn to_expr(&self) -> Expr<Self> {
 | 
			
		||||
        Expr::from_value(self)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Value for SyncReset {
 | 
			
		||||
    fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue {
 | 
			
		||||
        *self
 | 
			
		||||
    }
 | 
			
		||||
    fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
 | 
			
		||||
        interned_bit(this.0)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl CanonicalValue for SyncReset {
 | 
			
		||||
    fn value_enum_impl(this: &Self) -> ValueEnum {
 | 
			
		||||
        ValueEnum::SyncReset(*this)
 | 
			
		||||
    }
 | 
			
		||||
    fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
 | 
			
		||||
        interned_bit(this.0)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Default)]
 | 
			
		||||
pub struct ResetType;
 | 
			
		||||
 | 
			
		||||
impl ResetType {
 | 
			
		||||
    pub const fn new() -> Self {
 | 
			
		||||
        Self
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Type for ResetType {
 | 
			
		||||
    type Value = Reset;
 | 
			
		||||
    type CanonicalType = ResetType;
 | 
			
		||||
    type CanonicalValue = Reset;
 | 
			
		||||
    type MaskType = UIntType<1>;
 | 
			
		||||
    type MaskValue = UInt<1>;
 | 
			
		||||
 | 
			
		||||
    impl_match_values_as_self!();
 | 
			
		||||
 | 
			
		||||
    fn mask_type(&self) -> Self::MaskType {
 | 
			
		||||
        UIntType::new()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn canonical(&self) -> Self::CanonicalType {
 | 
			
		||||
        *self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn source_location(&self) -> SourceLocation {
 | 
			
		||||
        SourceLocation::builtin()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn type_enum(&self) -> TypeEnum {
 | 
			
		||||
        TypeEnum::Reset(*self)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn from_canonical_type(t: Self::CanonicalType) -> Self {
 | 
			
		||||
        t
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> {
 | 
			
		||||
        Some(this)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Connect<Self> for ResetType {}
 | 
			
		||||
 | 
			
		||||
impl CanonicalType for ResetType {
 | 
			
		||||
    const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::Reset;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl FixedType for ResetType {
 | 
			
		||||
    fn fixed_type() -> Self {
 | 
			
		||||
        Self
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ResetTypeTrait for ResetType {}
 | 
			
		||||
 | 
			
		||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
 | 
			
		||||
pub enum Reset {}
 | 
			
		||||
 | 
			
		||||
impl ToExpr for Reset {
 | 
			
		||||
    type Type = ResetType;
 | 
			
		||||
 | 
			
		||||
    fn ty(&self) -> Self::Type {
 | 
			
		||||
        match *self {}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn to_expr(&self) -> Expr<Self> {
 | 
			
		||||
        Expr::from_value(self)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Value for Reset {
 | 
			
		||||
    fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue {
 | 
			
		||||
        *self
 | 
			
		||||
    }
 | 
			
		||||
    fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
 | 
			
		||||
        match *this {}
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl CanonicalValue for Reset {
 | 
			
		||||
    fn value_enum_impl(this: &Self) -> ValueEnum {
 | 
			
		||||
        ValueEnum::Reset(*this)
 | 
			
		||||
    }
 | 
			
		||||
    fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
 | 
			
		||||
        match *this {}
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
macro_rules! make_to_reset {
 | 
			
		||||
    (
 | 
			
		||||
        $(#[from_value($from_value_ty:ty)])*
 | 
			
		||||
        $vis:vis trait $Trait:ident {
 | 
			
		||||
            fn $fn:ident(&self) -> Expr<$T:ty>;
 | 
			
		||||
        }
 | 
			
		||||
    ) => {
 | 
			
		||||
        $vis trait $Trait {
 | 
			
		||||
            fn $fn(&self) -> Expr<$T>;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        impl<T: ?Sized + $Trait> $Trait for &'_ T {
 | 
			
		||||
            fn $fn(&self) -> Expr<$T> {
 | 
			
		||||
                (**self).$fn()
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        impl<T: ?Sized + $Trait> $Trait for &'_ mut T {
 | 
			
		||||
            fn $fn(&self) -> Expr<$T> {
 | 
			
		||||
                (**self).$fn()
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        impl<T: ?Sized + $Trait> $Trait for Box<T> {
 | 
			
		||||
            fn $fn(&self) -> Expr<$T> {
 | 
			
		||||
                (**self).$fn()
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        impl $Trait for Expr<$T> {
 | 
			
		||||
            fn $fn(&self) -> Expr<$T> {
 | 
			
		||||
                *self
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        impl $Trait for $T {
 | 
			
		||||
            fn $fn(&self) -> Expr<$T> {
 | 
			
		||||
                self.to_expr()
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $(impl $Trait for $from_value_ty {
 | 
			
		||||
            fn $fn(&self) -> Expr<$T> {
 | 
			
		||||
                self.to_expr().$fn()
 | 
			
		||||
            }
 | 
			
		||||
        })*
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
make_to_reset! {
 | 
			
		||||
    #[from_value(SyncReset)]
 | 
			
		||||
    #[from_value(AsyncReset)]
 | 
			
		||||
    pub trait ToReset {
 | 
			
		||||
        fn to_reset(&self) -> Expr<Reset>;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
make_to_reset! {
 | 
			
		||||
    #[from_value(bool)]
 | 
			
		||||
    #[from_value(UInt<1>)]
 | 
			
		||||
    pub trait ToAsyncReset {
 | 
			
		||||
        fn to_async_reset(&self) -> Expr<AsyncReset>;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
make_to_reset! {
 | 
			
		||||
    #[from_value(bool)]
 | 
			
		||||
    #[from_value(UInt<1>)]
 | 
			
		||||
    pub trait ToSyncReset {
 | 
			
		||||
        fn to_sync_reset(&self) -> Expr<SyncReset>;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										203
									
								
								crates/fayalite/src/source_location.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										203
									
								
								crates/fayalite/src/source_location.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,203 @@
 | 
			
		|||
// SPDX-License-Identifier: LGPL-3.0-or-later
 | 
			
		||||
// See Notices.txt for copyright information
 | 
			
		||||
use crate::{
 | 
			
		||||
    intern::{Intern, Interned},
 | 
			
		||||
    util::DebugAsDisplay,
 | 
			
		||||
};
 | 
			
		||||
use hashbrown::HashMap;
 | 
			
		||||
use std::{cell::RefCell, fmt, num::NonZeroUsize, panic, path::Path};
 | 
			
		||||
 | 
			
		||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
 | 
			
		||||
pub struct SourceLocation {
 | 
			
		||||
    file: Interned<str>,
 | 
			
		||||
    line: u32,
 | 
			
		||||
    column: u32,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl fmt::Debug for SourceLocation {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        f.debug_tuple("SourceLocation")
 | 
			
		||||
            .field(&DebugAsDisplay(self))
 | 
			
		||||
            .finish()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl fmt::Display for SourceLocation {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        let Self { file, line, column } = *self;
 | 
			
		||||
        write!(f, "{file}:{line}:{column}")
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Copy, Clone, Debug)]
 | 
			
		||||
struct FilePattern(&'static str);
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
 | 
			
		||||
struct FilePatternMatch {
 | 
			
		||||
    prefix: String,
 | 
			
		||||
    suffix: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<&'_ FilePatternMatch> for FilePatternMatch {
 | 
			
		||||
    fn from(value: &'_ FilePatternMatch) -> Self {
 | 
			
		||||
        value.clone()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl FilePatternMatch {
 | 
			
		||||
    fn generate_file_name(&self, file_name_id: NonZeroUsize) -> String {
 | 
			
		||||
        if file_name_id.get() == 1 {
 | 
			
		||||
            self.prefix.clone() + &self.suffix
 | 
			
		||||
        } else {
 | 
			
		||||
            format!("{}-{file_name_id}{}", self.prefix, self.suffix)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl FilePattern {
 | 
			
		||||
    const TEMPLATE_CHAR: char = 'X';
 | 
			
		||||
    fn matches(self, file_name: &str) -> Option<FilePatternMatch> {
 | 
			
		||||
        if self.0.len() != file_name.len() {
 | 
			
		||||
            return None;
 | 
			
		||||
        }
 | 
			
		||||
        let mut prefix = String::with_capacity(file_name.len());
 | 
			
		||||
        for (pattern_ch, ch) in self.0.chars().zip(file_name.chars()) {
 | 
			
		||||
            if pattern_ch == Self::TEMPLATE_CHAR {
 | 
			
		||||
                if !ch.is_ascii_hexdigit() {
 | 
			
		||||
                    return None;
 | 
			
		||||
                } else {
 | 
			
		||||
                    prefix.push(Self::TEMPLATE_CHAR);
 | 
			
		||||
                }
 | 
			
		||||
            } else if pattern_ch != ch {
 | 
			
		||||
                return None;
 | 
			
		||||
            } else {
 | 
			
		||||
                prefix.push(ch);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        let split_point = self.0.rfind('.').unwrap_or(self.0.len());
 | 
			
		||||
        Some(FilePatternMatch {
 | 
			
		||||
            suffix: prefix.split_off(split_point),
 | 
			
		||||
            prefix,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct NormalizedFileForTestState {
 | 
			
		||||
    file_name_id: NonZeroUsize,
 | 
			
		||||
    positions_map: HashMap<(u32, u32), u32>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct NormalizeFilesForTestsState {
 | 
			
		||||
    test_position: &'static panic::Location<'static>,
 | 
			
		||||
    file_pattern_matches: HashMap<FilePatternMatch, HashMap<String, NormalizedFileForTestState>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl NormalizeFilesForTestsState {
 | 
			
		||||
    #[track_caller]
 | 
			
		||||
    fn new() -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            test_position: panic::Location::caller(),
 | 
			
		||||
            file_pattern_matches: HashMap::new(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const FILE_PATTERNS: &[FilePattern] = &[
 | 
			
		||||
    FilePattern("module-XXXXXXXXXX.rs"),
 | 
			
		||||
    FilePattern("value-struct-XXXXXXXXXX.rs"),
 | 
			
		||||
    FilePattern("value-enum-XXXXXXXXXX.rs"),
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
thread_local! {
 | 
			
		||||
    static NORMALIZE_FILES_FOR_TESTS: RefCell<Option<NormalizeFilesForTestsState>> = const { RefCell::new(None) };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<&'_ panic::Location<'_>> for SourceLocation {
 | 
			
		||||
    fn from(value: &'_ panic::Location<'_>) -> Self {
 | 
			
		||||
        let mut file = value.file();
 | 
			
		||||
        let mut line = value.line();
 | 
			
		||||
        let mut column = value.column();
 | 
			
		||||
        let mut file_str = String::new();
 | 
			
		||||
        NORMALIZE_FILES_FOR_TESTS.with_borrow_mut(|state| {
 | 
			
		||||
            let Some(state) = state else {
 | 
			
		||||
                return;
 | 
			
		||||
            };
 | 
			
		||||
            if file == state.test_position.file() {
 | 
			
		||||
                file = "the_test_file.rs";
 | 
			
		||||
                line = line
 | 
			
		||||
                    .saturating_add(10000)
 | 
			
		||||
                    .saturating_sub(state.test_position.line());
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            file = Path::new(file)
 | 
			
		||||
                .file_name()
 | 
			
		||||
                .and_then(|v| v.to_str())
 | 
			
		||||
                .unwrap_or("<no-file-name>");
 | 
			
		||||
            for p in FILE_PATTERNS {
 | 
			
		||||
                if let Some(m) = p.matches(file) {
 | 
			
		||||
                    let map = state.file_pattern_matches.entry_ref(&m).or_default();
 | 
			
		||||
                    let len = map.len();
 | 
			
		||||
                    let file_state =
 | 
			
		||||
                        map.entry_ref(file)
 | 
			
		||||
                            .or_insert_with(|| NormalizedFileForTestState {
 | 
			
		||||
                                file_name_id: NonZeroUsize::new(len + 1).unwrap(),
 | 
			
		||||
                                positions_map: HashMap::new(),
 | 
			
		||||
                            });
 | 
			
		||||
                    file_str = m.generate_file_name(file_state.file_name_id);
 | 
			
		||||
                    file = &file_str;
 | 
			
		||||
                    let positions_len = file_state.positions_map.len();
 | 
			
		||||
                    line = *file_state
 | 
			
		||||
                        .positions_map
 | 
			
		||||
                        .entry((line, column))
 | 
			
		||||
                        .or_insert((positions_len + 1).try_into().unwrap());
 | 
			
		||||
                    column = 1;
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        Self {
 | 
			
		||||
            file: file.intern(),
 | 
			
		||||
            line,
 | 
			
		||||
            column,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[must_use = "resets value when dropped"]
 | 
			
		||||
pub struct NormalizeFilesForTestsScope {
 | 
			
		||||
    old_state: Option<NormalizeFilesForTestsState>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Drop for NormalizeFilesForTestsScope {
 | 
			
		||||
    fn drop(&mut self) {
 | 
			
		||||
        NORMALIZE_FILES_FOR_TESTS.set(self.old_state.take());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl SourceLocation {
 | 
			
		||||
    #[track_caller]
 | 
			
		||||
    pub fn normalize_files_for_tests() -> NormalizeFilesForTestsScope {
 | 
			
		||||
        NormalizeFilesForTestsScope {
 | 
			
		||||
            old_state: NORMALIZE_FILES_FOR_TESTS.replace(Some(NormalizeFilesForTestsState::new())),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    #[track_caller]
 | 
			
		||||
    pub fn caller() -> Self {
 | 
			
		||||
        panic::Location::caller().into()
 | 
			
		||||
    }
 | 
			
		||||
    pub(crate) fn builtin() -> Self {
 | 
			
		||||
        Self::new("builtin".intern(), 1, 1)
 | 
			
		||||
    }
 | 
			
		||||
    pub const fn new(file: Interned<str>, line: u32, column: u32) -> Self {
 | 
			
		||||
        Self { file, line, column }
 | 
			
		||||
    }
 | 
			
		||||
    pub const fn file(self) -> Interned<str> {
 | 
			
		||||
        self.file
 | 
			
		||||
    }
 | 
			
		||||
    pub const fn line(self) -> u32 {
 | 
			
		||||
        self.line
 | 
			
		||||
    }
 | 
			
		||||
    pub const fn column(self) -> u32 {
 | 
			
		||||
        self.column
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1007
									
								
								crates/fayalite/src/ty.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1007
									
								
								crates/fayalite/src/ty.rs
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										267
									
								
								crates/fayalite/src/util.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										267
									
								
								crates/fayalite/src/util.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,267 @@
 | 
			
		|||
// SPDX-License-Identifier: LGPL-3.0-or-later
 | 
			
		||||
// See Notices.txt for copyright information
 | 
			
		||||
use crate::intern::{Intern, Interned};
 | 
			
		||||
use bitvec::{bits, order::Lsb0, slice::BitSlice, view::BitView};
 | 
			
		||||
use std::{
 | 
			
		||||
    cmp::Ordering,
 | 
			
		||||
    fmt::{self, Debug, Write},
 | 
			
		||||
    hash::Hash,
 | 
			
		||||
    mem::ManuallyDrop,
 | 
			
		||||
    ptr,
 | 
			
		||||
    rc::Rc,
 | 
			
		||||
    sync::{Arc, OnceLock},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
mod sealed {
 | 
			
		||||
    pub trait Sealed {}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// # Safety
 | 
			
		||||
/// the only implementation is `ConstBool<Self::VALUE>`
 | 
			
		||||
pub unsafe trait GenericConstBool:
 | 
			
		||||
    sealed::Sealed + Copy + Ord + Hash + Default + Debug + 'static + Send + Sync
 | 
			
		||||
{
 | 
			
		||||
    const VALUE: bool;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
 | 
			
		||||
pub struct ConstBool<const VALUE: bool>;
 | 
			
		||||
 | 
			
		||||
impl<const VALUE: bool> Debug for ConstBool<VALUE> {
 | 
			
		||||
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 | 
			
		||||
        f.debug_tuple("ConstBool").field(&Self::VALUE).finish()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<const VALUE: bool> sealed::Sealed for ConstBool<VALUE> {}
 | 
			
		||||
 | 
			
		||||
/// SAFETY: the only implementation is `ConstBool<Self::VALUE>`
 | 
			
		||||
unsafe impl<const VALUE: bool> GenericConstBool for ConstBool<VALUE> {
 | 
			
		||||
    const VALUE: bool = VALUE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub trait ConstBoolDispatchTag {
 | 
			
		||||
    type Type<Select: GenericConstBool>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub enum ConstBoolDispatch<FalseT, TrueT> {
 | 
			
		||||
    False(FalseT),
 | 
			
		||||
    True(TrueT),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<FalseT, TrueT> ConstBoolDispatch<FalseT, TrueT> {
 | 
			
		||||
    pub fn new<
 | 
			
		||||
        Tag: ConstBoolDispatchTag<Type<ConstBool<false>> = FalseT>
 | 
			
		||||
            + ConstBoolDispatchTag<Type<ConstBool<true>> = TrueT>,
 | 
			
		||||
        Select: GenericConstBool,
 | 
			
		||||
    >(
 | 
			
		||||
        v: Tag::Type<Select>,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        let v = ManuallyDrop::new(v);
 | 
			
		||||
        let v_ptr: *const Tag::Type<Select> = &*v;
 | 
			
		||||
        // SAFETY: reads the exact same type, since Select is really ConstBool<Select::VALUE>
 | 
			
		||||
        unsafe {
 | 
			
		||||
            if Select::VALUE {
 | 
			
		||||
                ConstBoolDispatch::True(ptr::read(v_ptr.cast()))
 | 
			
		||||
            } else {
 | 
			
		||||
                ConstBoolDispatch::False(ptr::read(v_ptr.cast()))
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// replacement for Iterator::eq_by which isn't stable yet
 | 
			
		||||
pub fn iter_eq_by<L: IntoIterator, R: IntoIterator, F: FnMut(L::Item, R::Item) -> bool>(
 | 
			
		||||
    l: L,
 | 
			
		||||
    r: R,
 | 
			
		||||
    mut f: F,
 | 
			
		||||
) -> bool {
 | 
			
		||||
    let mut l = l.into_iter();
 | 
			
		||||
    let mut r = r.into_iter();
 | 
			
		||||
    l.try_for_each(|l| {
 | 
			
		||||
        if let Some(r) = r.next() {
 | 
			
		||||
            f(l, r).then_some(())
 | 
			
		||||
        } else {
 | 
			
		||||
            None
 | 
			
		||||
        }
 | 
			
		||||
    })
 | 
			
		||||
    .is_some()
 | 
			
		||||
        && r.next().is_none()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct DebugAsDisplay<T>(pub T);
 | 
			
		||||
 | 
			
		||||
impl<T: fmt::Display> fmt::Debug for DebugAsDisplay<T> {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        self.0.fmt(f)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub const fn const_u8_cmp(a: u8, b: u8) -> Ordering {
 | 
			
		||||
    if a < b {
 | 
			
		||||
        Ordering::Less
 | 
			
		||||
    } else if a > b {
 | 
			
		||||
        Ordering::Greater
 | 
			
		||||
    } else {
 | 
			
		||||
        Ordering::Equal
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub const fn const_usize_cmp(a: usize, b: usize) -> Ordering {
 | 
			
		||||
    if a < b {
 | 
			
		||||
        Ordering::Less
 | 
			
		||||
    } else if a > b {
 | 
			
		||||
        Ordering::Greater
 | 
			
		||||
    } else {
 | 
			
		||||
        Ordering::Equal
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub const fn const_bytes_cmp(a: &[u8], b: &[u8]) -> Ordering {
 | 
			
		||||
    let mut i = 0;
 | 
			
		||||
    while i < a.len() && i < b.len() {
 | 
			
		||||
        match const_u8_cmp(a[i], b[i]) {
 | 
			
		||||
            Ordering::Equal => {}
 | 
			
		||||
            retval => return retval,
 | 
			
		||||
        }
 | 
			
		||||
        i += 1;
 | 
			
		||||
    }
 | 
			
		||||
    const_usize_cmp(a.len(), b.len())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub const fn const_str_cmp(a: &str, b: &str) -> Ordering {
 | 
			
		||||
    const_bytes_cmp(a.as_bytes(), b.as_bytes())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub const fn const_str_array_is_strictly_ascending(a: &[&str]) -> bool {
 | 
			
		||||
    let mut i = 1;
 | 
			
		||||
    while i < a.len() {
 | 
			
		||||
        match const_str_cmp(a[i - 1], a[i]) {
 | 
			
		||||
            Ordering::Less => {}
 | 
			
		||||
            _ => return false,
 | 
			
		||||
        }
 | 
			
		||||
        i += 1;
 | 
			
		||||
    }
 | 
			
		||||
    true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub trait MakeMutSlice {
 | 
			
		||||
    type Element: Clone;
 | 
			
		||||
    fn make_mut_slice(&mut self) -> &mut [Self::Element];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: Clone> MakeMutSlice for Arc<[T]> {
 | 
			
		||||
    type Element = T;
 | 
			
		||||
 | 
			
		||||
    fn make_mut_slice(&mut self) -> &mut [Self::Element] {
 | 
			
		||||
        if Arc::get_mut(self).is_none() {
 | 
			
		||||
            *self = Arc::<[T]>::from(&**self);
 | 
			
		||||
        }
 | 
			
		||||
        Arc::get_mut(self).unwrap()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: Clone> MakeMutSlice for Rc<[T]> {
 | 
			
		||||
    type Element = T;
 | 
			
		||||
 | 
			
		||||
    fn make_mut_slice(&mut self) -> &mut [Self::Element] {
 | 
			
		||||
        if Rc::get_mut(self).is_none() {
 | 
			
		||||
            *self = Rc::<[T]>::from(&**self);
 | 
			
		||||
        }
 | 
			
		||||
        Rc::get_mut(self).unwrap()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct DebugAsRawString<'a>(pub &'a str);
 | 
			
		||||
 | 
			
		||||
impl fmt::Debug for DebugAsRawString<'_> {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        let pounds = self
 | 
			
		||||
            .0
 | 
			
		||||
            .split_inclusive('"')
 | 
			
		||||
            .skip(1)
 | 
			
		||||
            .map(|v| v.len() - v.trim_start_matches('#').len())
 | 
			
		||||
            .max()
 | 
			
		||||
            .map(|v| v + 1)
 | 
			
		||||
            .unwrap_or(0);
 | 
			
		||||
        f.write_char('r')?;
 | 
			
		||||
        for _ in 0..pounds {
 | 
			
		||||
            f.write_char('#')?;
 | 
			
		||||
        }
 | 
			
		||||
        f.write_char('"')?;
 | 
			
		||||
        f.write_str(self.0)?;
 | 
			
		||||
        f.write_char('"')?;
 | 
			
		||||
        for _ in 0..pounds {
 | 
			
		||||
            f.write_char('#')?;
 | 
			
		||||
        }
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn interned_bit(v: bool) -> Interned<BitSlice> {
 | 
			
		||||
    static RETVAL: OnceLock<[Interned<BitSlice>; 2]> = OnceLock::new();
 | 
			
		||||
    // use bits![V; 1] instead of bits![V] to work around https://github.com/ferrilab/bitvec/issues/271
 | 
			
		||||
    RETVAL.get_or_init(|| [bits![0; 1].intern(), bits![1; 1].intern()])[v as usize]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Copy, Clone, Debug)]
 | 
			
		||||
pub struct BitSliceWriteWithBase<'a>(pub &'a BitSlice);
 | 
			
		||||
 | 
			
		||||
impl BitSliceWriteWithBase<'_> {
 | 
			
		||||
    fn fmt_with_base<const BITS_PER_DIGIT: usize, const UPPER_CASE: bool>(
 | 
			
		||||
        self,
 | 
			
		||||
        f: &mut fmt::Formatter<'_>,
 | 
			
		||||
    ) -> fmt::Result {
 | 
			
		||||
        let digit_count = self.0.len().div_ceil(BITS_PER_DIGIT).max(1);
 | 
			
		||||
        // TODO: optimize
 | 
			
		||||
        let mut buf = String::with_capacity(digit_count);
 | 
			
		||||
        for digit_index in (0..digit_count).rev() {
 | 
			
		||||
            let mut digit = 0;
 | 
			
		||||
            let src = self
 | 
			
		||||
                .0
 | 
			
		||||
                .get(digit_index * BITS_PER_DIGIT..)
 | 
			
		||||
                .unwrap_or_default();
 | 
			
		||||
            let src = src.get(..BITS_PER_DIGIT).unwrap_or(src);
 | 
			
		||||
            digit.view_bits_mut::<Lsb0>()[..src.len()].copy_from_bitslice(src);
 | 
			
		||||
            let mut ch = char::from_digit(digit as u32, 1 << BITS_PER_DIGIT).unwrap();
 | 
			
		||||
            if UPPER_CASE {
 | 
			
		||||
                ch = ch.to_ascii_uppercase();
 | 
			
		||||
            }
 | 
			
		||||
            buf.push(ch);
 | 
			
		||||
        }
 | 
			
		||||
        f.pad_integral(
 | 
			
		||||
            true,
 | 
			
		||||
            match BITS_PER_DIGIT {
 | 
			
		||||
                1 => "0b",
 | 
			
		||||
                3 => "0o",
 | 
			
		||||
                4 => "0x",
 | 
			
		||||
                _ => "",
 | 
			
		||||
            },
 | 
			
		||||
            &buf,
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl fmt::Binary for BitSliceWriteWithBase<'_> {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        self.fmt_with_base::<1, false>(f)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl fmt::Octal for BitSliceWriteWithBase<'_> {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        self.fmt_with_base::<3, false>(f)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl fmt::LowerHex for BitSliceWriteWithBase<'_> {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        self.fmt_with_base::<4, false>(f)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl fmt::UpperHex for BitSliceWriteWithBase<'_> {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        self.fmt_with_base::<4, true>(f)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										89
									
								
								crates/fayalite/src/valueless.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								crates/fayalite/src/valueless.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,89 @@
 | 
			
		|||
// SPDX-License-Identifier: LGPL-3.0-or-later
 | 
			
		||||
// See Notices.txt for copyright information
 | 
			
		||||
use crate::{
 | 
			
		||||
    int::{DynIntType, DynSIntType, DynUIntType, IntTypeTrait, SIntType},
 | 
			
		||||
    ty::{Type, Value},
 | 
			
		||||
};
 | 
			
		||||
use std::ops::RangeBounds;
 | 
			
		||||
 | 
			
		||||
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Default)]
 | 
			
		||||
pub struct Valueless<T> {
 | 
			
		||||
    pub ty: T,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: Type> Valueless<T> {
 | 
			
		||||
    pub fn to_canonical(&self) -> Valueless<T::CanonicalType> {
 | 
			
		||||
        Valueless {
 | 
			
		||||
            ty: self.ty.canonical(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn from_canonical(v: Valueless<T::CanonicalType>) -> Self {
 | 
			
		||||
        Valueless {
 | 
			
		||||
            ty: T::from_canonical_type(v.ty),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
mod sealed {
 | 
			
		||||
    pub trait Sealed {}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub trait ValuelessTr: sealed::Sealed {
 | 
			
		||||
    type Type: Type<Value = Self::Value>;
 | 
			
		||||
    type Value: Value<Type = Self::Type>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T> sealed::Sealed for Valueless<T> {}
 | 
			
		||||
 | 
			
		||||
impl<T: Type> ValuelessTr for Valueless<T> {
 | 
			
		||||
    type Type = T;
 | 
			
		||||
    type Value = T::Value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: IntTypeTrait> Valueless<T> {
 | 
			
		||||
    pub fn signum(&self) -> Valueless<SIntType<2>> {
 | 
			
		||||
        Valueless::default()
 | 
			
		||||
    }
 | 
			
		||||
    pub fn as_same_width_uint(self) -> Valueless<T::SameWidthUInt> {
 | 
			
		||||
        Valueless {
 | 
			
		||||
            ty: self.ty.as_same_width_uint(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn as_same_width_sint(self) -> Valueless<T::SameWidthSInt> {
 | 
			
		||||
        Valueless {
 | 
			
		||||
            ty: self.ty.as_same_width_sint(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn as_same_value_uint(self) -> Valueless<DynUIntType> {
 | 
			
		||||
        Valueless {
 | 
			
		||||
            ty: self.ty.as_same_value_uint(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn as_same_value_sint(self) -> Valueless<DynSIntType> {
 | 
			
		||||
        Valueless {
 | 
			
		||||
            ty: self.ty.as_same_value_sint(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn concat<HighType: IntTypeTrait>(
 | 
			
		||||
        &self,
 | 
			
		||||
        high_part: Valueless<HighType>,
 | 
			
		||||
    ) -> Valueless<DynIntType<HighType::Signed>> {
 | 
			
		||||
        let self_type = self.ty.canonical();
 | 
			
		||||
        let ty = DynIntType::new(
 | 
			
		||||
            self_type
 | 
			
		||||
                .width
 | 
			
		||||
                .checked_add(high_part.ty.width())
 | 
			
		||||
                .expect("result has too many bits"),
 | 
			
		||||
        );
 | 
			
		||||
        Valueless { ty }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn repeat(&self, count: usize) -> Valueless<DynIntType<T::Signed>> {
 | 
			
		||||
        let width = self.ty.width();
 | 
			
		||||
        let ty = DynIntType::new(width.checked_mul(count).expect("result has too many bits"));
 | 
			
		||||
        Valueless { ty }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn slice<I: RangeBounds<usize>>(&self, index: I) -> Valueless<DynUIntType> {
 | 
			
		||||
        let ty = self.ty.slice(index);
 | 
			
		||||
        Valueless { ty }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										112
									
								
								crates/fayalite/src/wire.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								crates/fayalite/src/wire.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,112 @@
 | 
			
		|||
// SPDX-License-Identifier: LGPL-3.0-or-later
 | 
			
		||||
// See Notices.txt for copyright information
 | 
			
		||||
use crate::{
 | 
			
		||||
    expr::{Expr, ExprTrait, Flow, ToExpr},
 | 
			
		||||
    intern::Interned,
 | 
			
		||||
    module::{NameId, ScopedNameId},
 | 
			
		||||
    source_location::SourceLocation,
 | 
			
		||||
    ty::{DynCanonicalType, DynType, Type},
 | 
			
		||||
};
 | 
			
		||||
use std::fmt;
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Eq, PartialEq, Hash)]
 | 
			
		||||
pub struct Wire<T: Type> {
 | 
			
		||||
    name: ScopedNameId,
 | 
			
		||||
    source_location: SourceLocation,
 | 
			
		||||
    ty: T,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: Type + fmt::Debug> fmt::Debug for Wire<T> {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        f.debug_struct("Wire")
 | 
			
		||||
            .field("name", &self.name)
 | 
			
		||||
            .field("ty", &self.ty)
 | 
			
		||||
            .finish_non_exhaustive()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: Type> ToExpr for Wire<T> {
 | 
			
		||||
    type Type = T;
 | 
			
		||||
 | 
			
		||||
    fn ty(&self) -> Self::Type {
 | 
			
		||||
        self.ty.clone()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
 | 
			
		||||
        Expr::new_unchecked(self.expr_enum())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: Type> Wire<T> {
 | 
			
		||||
    pub fn canonical(&self) -> Wire<T::CanonicalType> {
 | 
			
		||||
        let Self {
 | 
			
		||||
            name,
 | 
			
		||||
            source_location,
 | 
			
		||||
            ref ty,
 | 
			
		||||
        } = *self;
 | 
			
		||||
        Wire {
 | 
			
		||||
            name,
 | 
			
		||||
            source_location,
 | 
			
		||||
            ty: ty.canonical(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn to_dyn_wire(&self) -> Wire<Interned<dyn DynType>> {
 | 
			
		||||
        let Self {
 | 
			
		||||
            name,
 | 
			
		||||
            source_location,
 | 
			
		||||
            ref ty,
 | 
			
		||||
        } = *self;
 | 
			
		||||
        Wire {
 | 
			
		||||
            name,
 | 
			
		||||
            source_location,
 | 
			
		||||
            ty: ty.to_dyn(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn to_dyn_canonical_wire(&self) -> Wire<Interned<dyn DynCanonicalType>> {
 | 
			
		||||
        let Self {
 | 
			
		||||
            name,
 | 
			
		||||
            source_location,
 | 
			
		||||
            ref ty,
 | 
			
		||||
        } = *self;
 | 
			
		||||
        Wire {
 | 
			
		||||
            name,
 | 
			
		||||
            source_location,
 | 
			
		||||
            ty: ty.canonical_dyn(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn new_unchecked(
 | 
			
		||||
        scoped_name: ScopedNameId,
 | 
			
		||||
        source_location: SourceLocation,
 | 
			
		||||
        ty: T,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            name: scoped_name,
 | 
			
		||||
            source_location,
 | 
			
		||||
            ty,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pub fn source_location(&self) -> SourceLocation {
 | 
			
		||||
        self.source_location
 | 
			
		||||
    }
 | 
			
		||||
    pub fn containing_module_name(&self) -> Interned<str> {
 | 
			
		||||
        self.containing_module_name_id().0
 | 
			
		||||
    }
 | 
			
		||||
    pub fn containing_module_name_id(&self) -> NameId {
 | 
			
		||||
        self.name.0
 | 
			
		||||
    }
 | 
			
		||||
    pub fn name(&self) -> Interned<str> {
 | 
			
		||||
        self.name_id().0
 | 
			
		||||
    }
 | 
			
		||||
    pub fn name_id(&self) -> NameId {
 | 
			
		||||
        self.name.1
 | 
			
		||||
    }
 | 
			
		||||
    pub fn scoped_name(&self) -> ScopedNameId {
 | 
			
		||||
        self.name
 | 
			
		||||
    }
 | 
			
		||||
    pub fn flow(&self) -> Flow {
 | 
			
		||||
        Flow::Duplex
 | 
			
		||||
    }
 | 
			
		||||
    pub fn must_connect_to(&self) -> bool {
 | 
			
		||||
        true
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										3065
									
								
								crates/fayalite/tests/module.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3065
									
								
								crates/fayalite/tests/module.rs
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										7
									
								
								crates/fayalite/tests/ui.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								crates/fayalite/tests/ui.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
// SPDX-License-Identifier: LGPL-3.0-or-later
 | 
			
		||||
// See Notices.txt for copyright information
 | 
			
		||||
#[test]
 | 
			
		||||
fn ui() {
 | 
			
		||||
    let t = trybuild::TestCases::new();
 | 
			
		||||
    t.compile_fail("tests/ui/*.rs");
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										14
									
								
								crates/fayalite/tests/ui/module.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								crates/fayalite/tests/ui/module.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,14 @@
 | 
			
		|||
// SPDX-License-Identifier: LGPL-3.0-or-later
 | 
			
		||||
// See Notices.txt for copyright information
 | 
			
		||||
#[allow(unused_imports)]
 | 
			
		||||
use fayalite::{hdl_module, int::UInt};
 | 
			
		||||
 | 
			
		||||
#[hdl_module]
 | 
			
		||||
pub fn my_module(a: i32, m: u32, (m, _): (i32, u32)) {
 | 
			
		||||
    #[hdl]
 | 
			
		||||
    let m: UInt<8> = m.input();
 | 
			
		||||
    #[hdl]
 | 
			
		||||
    let o: UInt<8> = m.output();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn main() {}
 | 
			
		||||
							
								
								
									
										17
									
								
								crates/fayalite/tests/ui/module.stderr
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								crates/fayalite/tests/ui/module.stderr
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
error: name conflicts with implicit `m: &mut ModuleBuilder<_>`
 | 
			
		||||
 --> tests/ui/module.rs:7:26
 | 
			
		||||
  |
 | 
			
		||||
7 | pub fn my_module(a: i32, m: u32, (m, _): (i32, u32)) {
 | 
			
		||||
  |                          ^
 | 
			
		||||
 | 
			
		||||
error: name conflicts with implicit `m: &mut ModuleBuilder<_>`
 | 
			
		||||
 --> tests/ui/module.rs:7:35
 | 
			
		||||
  |
 | 
			
		||||
7 | pub fn my_module(a: i32, m: u32, (m, _): (i32, u32)) {
 | 
			
		||||
  |                                   ^
 | 
			
		||||
 | 
			
		||||
error: name conflicts with implicit `m: &mut ModuleBuilder<_>`
 | 
			
		||||
 --> tests/ui/module.rs:9:9
 | 
			
		||||
  |
 | 
			
		||||
9 |     let m: UInt<8> = m.input();
 | 
			
		||||
  |         ^
 | 
			
		||||
							
								
								
									
										10
									
								
								crates/fayalite/tests/ui/value_derive.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								crates/fayalite/tests/ui/value_derive.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,10 @@
 | 
			
		|||
// SPDX-License-Identifier: LGPL-3.0-or-later
 | 
			
		||||
// See Notices.txt for copyright information
 | 
			
		||||
use fayalite::Value;
 | 
			
		||||
 | 
			
		||||
#[derive(Value)]
 | 
			
		||||
union U {
 | 
			
		||||
    a: (),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn main() {}
 | 
			
		||||
							
								
								
									
										7
									
								
								crates/fayalite/tests/ui/value_derive.stderr
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								crates/fayalite/tests/ui/value_derive.stderr
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
error: derive(Value) can only be used on structs or enums
 | 
			
		||||
 --> tests/ui/value_derive.rs:5:10
 | 
			
		||||
  |
 | 
			
		||||
5 | #[derive(Value)]
 | 
			
		||||
  |          ^^^^^
 | 
			
		||||
  |
 | 
			
		||||
  = note: this error originates in the derive macro `Value` (in Nightly builds, run with -Z macro-backtrace for more info)
 | 
			
		||||
							
								
								
									
										23
									
								
								crates/fayalite/tests/value_derive.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								crates/fayalite/tests/value_derive.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,23 @@
 | 
			
		|||
// SPDX-License-Identifier: LGPL-3.0-or-later
 | 
			
		||||
// See Notices.txt for copyright information
 | 
			
		||||
use fayalite::{int::UInt, Value};
 | 
			
		||||
 | 
			
		||||
#[derive(Value, Clone, Hash, PartialEq, Eq, Debug)]
 | 
			
		||||
#[hdl(outline_generated)]
 | 
			
		||||
pub struct S<T> {
 | 
			
		||||
    pub a: T,
 | 
			
		||||
    b: UInt<3>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Value, Clone, Hash, PartialEq, Eq, Debug)]
 | 
			
		||||
#[hdl(outline_generated)]
 | 
			
		||||
pub enum E<T> {
 | 
			
		||||
    A,
 | 
			
		||||
    B {},
 | 
			
		||||
    C(),
 | 
			
		||||
    D(UInt<3>),
 | 
			
		||||
    E { a: UInt<3> },
 | 
			
		||||
    F(UInt<3>, UInt<3>),
 | 
			
		||||
    G(T),
 | 
			
		||||
    H(T, UInt<1>),
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										914
									
								
								crates/fayalite/visit_types.json
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										914
									
								
								crates/fayalite/visit_types.json
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,914 @@
 | 
			
		|||
{
 | 
			
		||||
    "types": {
 | 
			
		||||
        "Module": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "Module::new_unchecked",
 | 
			
		||||
                "name_id()": "Visible",
 | 
			
		||||
                "source_location()": "Visible",
 | 
			
		||||
                "body()": "Visible",
 | 
			
		||||
                "module_io()": "Visible",
 | 
			
		||||
                "module_annotations()": "Visible"
 | 
			
		||||
            },
 | 
			
		||||
            "generics": {
 | 
			
		||||
                "generics": "<T: BundleValue>",
 | 
			
		||||
                "where": "T::Type: BundleType<Value = T>"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "ModuleBody": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Enum",
 | 
			
		||||
                "Normal": "Visible",
 | 
			
		||||
                "Extern": "Visible"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "ModuleIO": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "ModuleIO::new_unchecked",
 | 
			
		||||
                "containing_module_name_id()": "Visible",
 | 
			
		||||
                "name()": "Visible",
 | 
			
		||||
                "source_location()": "Visible",
 | 
			
		||||
                "is_input()": "Visible",
 | 
			
		||||
                "ty()": "Visible"
 | 
			
		||||
            },
 | 
			
		||||
            "generics": "<T: Type>",
 | 
			
		||||
            "fold_where": "T: Fold<State>",
 | 
			
		||||
            "visit_where": "T: Visit<State>"
 | 
			
		||||
        },
 | 
			
		||||
        "InternedDynCanonicalType": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "ManualImpl"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "InternedDynType": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "ManualImpl"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "TypeEnum": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Enum",
 | 
			
		||||
                "BundleType": "Visible",
 | 
			
		||||
                "EnumType": "Visible",
 | 
			
		||||
                "ArrayType": "Visible",
 | 
			
		||||
                "UInt": "Visible",
 | 
			
		||||
                "SInt": "Visible",
 | 
			
		||||
                "Clock": "Visible",
 | 
			
		||||
                "AsyncReset": "Visible",
 | 
			
		||||
                "SyncReset": "Visible",
 | 
			
		||||
                "Reset": "Visible"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "DynBundleType": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "DynBundleType::new",
 | 
			
		||||
                "fields()": "Visible"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "FieldType": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "name": "Visible",
 | 
			
		||||
                "flipped": "Visible",
 | 
			
		||||
                "ty": "Visible"
 | 
			
		||||
            },
 | 
			
		||||
            "generics": "<T>",
 | 
			
		||||
            "fold_where": "T: Fold<State>",
 | 
			
		||||
            "visit_where": "T: Visit<State>"
 | 
			
		||||
        },
 | 
			
		||||
        "DynEnumType": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "DynEnumType::new",
 | 
			
		||||
                "variants()": "Visible"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "VariantType": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "name": "Visible",
 | 
			
		||||
                "ty": "Visible"
 | 
			
		||||
            },
 | 
			
		||||
            "generics": "<T>",
 | 
			
		||||
            "fold_where": "T: Fold<State>",
 | 
			
		||||
            "visit_where": "T: Visit<State>"
 | 
			
		||||
        },
 | 
			
		||||
        "ArrayType": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "ManualImpl"
 | 
			
		||||
            },
 | 
			
		||||
            "generics": "<VA: ValueArrayOrSlice + ?Sized>",
 | 
			
		||||
            "fold_where": "VA::ElementType: Fold<State>",
 | 
			
		||||
            "visit_where": "VA::ElementType: Visit<State>"
 | 
			
		||||
        },
 | 
			
		||||
        "DynIntType": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Opaque"
 | 
			
		||||
            },
 | 
			
		||||
            "generics": "<Signed: GenericConstBool>"
 | 
			
		||||
        },
 | 
			
		||||
        "IntType": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Opaque"
 | 
			
		||||
            },
 | 
			
		||||
            "generics": "<Signed: GenericConstBool, const WIDTH: usize>"
 | 
			
		||||
        },
 | 
			
		||||
        "ClockType": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "AsyncResetType": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "SyncResetType": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "ResetType": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "SourceLocation": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Opaque"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "NameId": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Opaque"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "ScopedNameId": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "0": "Visible",
 | 
			
		||||
                "1": "Visible"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "ExternModuleBody": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "verilog_name": "Visible",
 | 
			
		||||
                "parameters": "Visible"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "ExternModuleParameter": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "name": "Visible",
 | 
			
		||||
                "value": "Visible"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "ExternModuleParameterValue": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Enum",
 | 
			
		||||
                "Integer": "Visible",
 | 
			
		||||
                "String": "Visible",
 | 
			
		||||
                "RawVerilog": "Visible"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "NormalModuleBody": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "body": "Visible"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "Block": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "memories": "Visible",
 | 
			
		||||
                "stmts": "Visible"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "Mem": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "Mem::new_unchecked",
 | 
			
		||||
                "scoped_name()": "Visible",
 | 
			
		||||
                "source_location()": "Visible",
 | 
			
		||||
                "array_type()": "Visible",
 | 
			
		||||
                "initial_value()": "Opaque",
 | 
			
		||||
                "ports()": "Visible",
 | 
			
		||||
                "read_latency()": "Visible",
 | 
			
		||||
                "write_latency()": "Visible",
 | 
			
		||||
                "read_under_write()": "Visible",
 | 
			
		||||
                "port_annotations()": "Visible",
 | 
			
		||||
                "mem_annotations()": "Visible"
 | 
			
		||||
            },
 | 
			
		||||
            "generics": "<VA: ValueArrayOrSlice + ?Sized>",
 | 
			
		||||
            "fold_where": "VA::ElementType: Fold<State>",
 | 
			
		||||
            "visit_where": "VA::ElementType: Visit<State>"
 | 
			
		||||
        },
 | 
			
		||||
        "MemPort": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "MemPort::new_unchecked",
 | 
			
		||||
                "mem_name()": "Visible",
 | 
			
		||||
                "source_location()": "Visible",
 | 
			
		||||
                "port_name()": "Visible",
 | 
			
		||||
                "addr_type()": "Visible",
 | 
			
		||||
                "mem_element_type()": "Visible"
 | 
			
		||||
            },
 | 
			
		||||
            "generics": "<T: PortType>",
 | 
			
		||||
            "fold_where": "T::PortType: Fold<State>",
 | 
			
		||||
            "visit_where": "T::PortType: Visit<State>"
 | 
			
		||||
        },
 | 
			
		||||
        "PortName": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "kind": "Visible",
 | 
			
		||||
                "index": "Visible"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "PortKind": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Enum",
 | 
			
		||||
                "ReadOnly": null,
 | 
			
		||||
                "WriteOnly": null,
 | 
			
		||||
                "ReadWrite": null
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "Expr": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "ManualImpl"
 | 
			
		||||
            },
 | 
			
		||||
            "generics": {
 | 
			
		||||
                "generics": "<T: Value>",
 | 
			
		||||
                "where": "T::Type: Type<Value = T>"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "ExprEnum": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "ManualImpl"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "Literal": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "ManualImpl"
 | 
			
		||||
            },
 | 
			
		||||
            "generics": "<T: Type>",
 | 
			
		||||
            "fold_where": "T::CanonicalValue: Fold<State>",
 | 
			
		||||
            "visit_where": "T::CanonicalValue: Visit<State>"
 | 
			
		||||
        },
 | 
			
		||||
        "DynCanonicalValue": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "ManualImpl"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "ValueEnum": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Enum",
 | 
			
		||||
                "Bundle": "Visible",
 | 
			
		||||
                "Enum": "Visible",
 | 
			
		||||
                "Array": "Visible",
 | 
			
		||||
                "UInt": "Visible",
 | 
			
		||||
                "SInt": "Visible",
 | 
			
		||||
                "Clock": "Visible",
 | 
			
		||||
                "AsyncReset": "Visible",
 | 
			
		||||
                "SyncReset": "Visible",
 | 
			
		||||
                "Reset": "Visible"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "DynBundle": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "DynBundle::new",
 | 
			
		||||
                "ty()": "Visible",
 | 
			
		||||
                "fields()": "RefVisible"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "DynEnum": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "DynEnum::new_by_index",
 | 
			
		||||
                "ty()": "Visible",
 | 
			
		||||
                "variant_index()": "Visible",
 | 
			
		||||
                "variant_value()": "RefVisible"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "Array": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "Array::new",
 | 
			
		||||
                "element_ty()": "RefVisible",
 | 
			
		||||
                "value()": "RefVisible"
 | 
			
		||||
            },
 | 
			
		||||
            "generics": "<VA: ValueArrayOrSlice + ?Sized>",
 | 
			
		||||
            "fold_where": "Arc<VA>: Fold<State>, VA::ElementType: Fold<State>",
 | 
			
		||||
            "visit_where": "Arc<VA>: Visit<State>, VA::ElementType: Visit<State>"
 | 
			
		||||
        },
 | 
			
		||||
        "DynInt": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Opaque"
 | 
			
		||||
            },
 | 
			
		||||
            "generics": "<Signed: GenericConstBool>"
 | 
			
		||||
        },
 | 
			
		||||
        "Clock": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Opaque"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "AsyncReset": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Opaque"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "SyncReset": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Opaque"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "Reset": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Opaque"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "ops::ArrayLiteral": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "ops::ArrayLiteral::new_unchecked",
 | 
			
		||||
                "elements()": "Visible",
 | 
			
		||||
                "ty()": "Visible"
 | 
			
		||||
            },
 | 
			
		||||
            "generics": "<ArrayTy: ArrayTypeTrait>",
 | 
			
		||||
            "fold_where": "ArrayTy: Fold<State>",
 | 
			
		||||
            "visit_where": "ArrayTy: Visit<State>"
 | 
			
		||||
        },
 | 
			
		||||
        "ops::BundleLiteral": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "ops::BundleLiteral::new_unchecked",
 | 
			
		||||
                "fields()": "Visible",
 | 
			
		||||
                "ty()": "Visible"
 | 
			
		||||
            },
 | 
			
		||||
            "generics": {
 | 
			
		||||
                "generics": "<BundleTy>",
 | 
			
		||||
                "where": "BundleTy: BundleType, BundleTy::Value: BundleValue<Type = BundleTy>"
 | 
			
		||||
            },
 | 
			
		||||
            "fold_where": "BundleTy: Fold<State>",
 | 
			
		||||
            "visit_where": "BundleTy: Visit<State>"
 | 
			
		||||
        },
 | 
			
		||||
        "ops::EnumLiteral": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "ops::EnumLiteral::new_unchecked",
 | 
			
		||||
                "variant_value()": "Visible",
 | 
			
		||||
                "variant_index()": "Visible",
 | 
			
		||||
                "ty()": "Visible"
 | 
			
		||||
            },
 | 
			
		||||
            "generics": {
 | 
			
		||||
                "generics": "<EnumTy>",
 | 
			
		||||
                "where": "EnumTy: EnumType, EnumTy::Value: EnumValue<Type = EnumTy>"
 | 
			
		||||
            },
 | 
			
		||||
            "fold_where": "EnumTy: Fold<State>",
 | 
			
		||||
            "visit_where": "EnumTy: Visit<State>"
 | 
			
		||||
        },
 | 
			
		||||
        "ops::Not": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "ops::Not::new_unchecked",
 | 
			
		||||
                "arg": "Visible"
 | 
			
		||||
            },
 | 
			
		||||
            "generics": "<T: IntTypeTrait>"
 | 
			
		||||
        },
 | 
			
		||||
        "ops::Neg": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "ops::Neg::new_unchecked",
 | 
			
		||||
                "arg": "Visible"
 | 
			
		||||
            },
 | 
			
		||||
            "generics": "<T: IntTypeTrait<Signed = ConstBool<true>>>"
 | 
			
		||||
        },
 | 
			
		||||
        "ops::BitAnd": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "ops::BitAnd::new_unchecked",
 | 
			
		||||
                "lhs": "Visible",
 | 
			
		||||
                "rhs": "Visible"
 | 
			
		||||
            },
 | 
			
		||||
            "generics": {
 | 
			
		||||
                "generics": "<L, R>",
 | 
			
		||||
                "where": "L: IntTypeTrait, R: IntTypeTrait<Signed = L::Signed>"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "ops::BitOr": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "ops::BitOr::new_unchecked",
 | 
			
		||||
                "lhs": "Visible",
 | 
			
		||||
                "rhs": "Visible"
 | 
			
		||||
            },
 | 
			
		||||
            "generics": {
 | 
			
		||||
                "generics": "<L, R>",
 | 
			
		||||
                "where": "L: IntTypeTrait, R: IntTypeTrait<Signed = L::Signed>"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "ops::BitXor": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "ops::BitXor::new_unchecked",
 | 
			
		||||
                "lhs": "Visible",
 | 
			
		||||
                "rhs": "Visible"
 | 
			
		||||
            },
 | 
			
		||||
            "generics": {
 | 
			
		||||
                "generics": "<L, R>",
 | 
			
		||||
                "where": "L: IntTypeTrait, R: IntTypeTrait<Signed = L::Signed>"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "ops::Add": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "ops::Add::new_unchecked",
 | 
			
		||||
                "lhs": "Visible",
 | 
			
		||||
                "rhs": "Visible"
 | 
			
		||||
            },
 | 
			
		||||
            "generics": {
 | 
			
		||||
                "generics": "<L, R>",
 | 
			
		||||
                "where": "L: IntTypeTrait, R: IntTypeTrait<Signed = L::Signed>"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "ops::Sub": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "ops::Sub::new_unchecked",
 | 
			
		||||
                "lhs": "Visible",
 | 
			
		||||
                "rhs": "Visible"
 | 
			
		||||
            },
 | 
			
		||||
            "generics": {
 | 
			
		||||
                "generics": "<L, R>",
 | 
			
		||||
                "where": "L: IntTypeTrait, R: IntTypeTrait<Signed = L::Signed>"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "ops::Mul": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "ops::Mul::new_unchecked",
 | 
			
		||||
                "lhs": "Visible",
 | 
			
		||||
                "rhs": "Visible"
 | 
			
		||||
            },
 | 
			
		||||
            "generics": {
 | 
			
		||||
                "generics": "<L, R>",
 | 
			
		||||
                "where": "L: IntTypeTrait, R: IntTypeTrait<Signed = L::Signed>"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "ops::DynShl": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "ops::DynShl::new_unchecked",
 | 
			
		||||
                "lhs": "Visible",
 | 
			
		||||
                "rhs": "Visible"
 | 
			
		||||
            },
 | 
			
		||||
            "generics": "<LhsType: IntTypeTrait>"
 | 
			
		||||
        },
 | 
			
		||||
        "ops::DynShr": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "ops::DynShr::new_unchecked",
 | 
			
		||||
                "lhs": "Visible",
 | 
			
		||||
                "rhs": "Visible"
 | 
			
		||||
            },
 | 
			
		||||
            "generics": "<LhsType: IntTypeTrait>"
 | 
			
		||||
        },
 | 
			
		||||
        "ops::FixedShl": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "ops::FixedShl::new_unchecked",
 | 
			
		||||
                "lhs": "Visible",
 | 
			
		||||
                "rhs": "Visible"
 | 
			
		||||
            },
 | 
			
		||||
            "generics": "<LhsType: IntTypeTrait>"
 | 
			
		||||
        },
 | 
			
		||||
        "ops::FixedShr": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "ops::FixedShr::new_unchecked",
 | 
			
		||||
                "lhs": "Visible",
 | 
			
		||||
                "rhs": "Visible"
 | 
			
		||||
            },
 | 
			
		||||
            "generics": "<LhsType: IntTypeTrait>"
 | 
			
		||||
        },
 | 
			
		||||
        "ops::CmpLt": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "ops::CmpLt::new_unchecked",
 | 
			
		||||
                "lhs": "Visible",
 | 
			
		||||
                "rhs": "Visible"
 | 
			
		||||
            },
 | 
			
		||||
            "generics": "<LhsType: IntTypeTrait, RhsType: IntTypeTrait<Signed = LhsType::Signed>, Output: FixedOrDynIntType<1, Signed = ConstBool<false>>>"
 | 
			
		||||
        },
 | 
			
		||||
        "ops::CmpLe": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "ops::CmpLe::new_unchecked",
 | 
			
		||||
                "lhs": "Visible",
 | 
			
		||||
                "rhs": "Visible"
 | 
			
		||||
            },
 | 
			
		||||
            "generics": "<LhsType: IntTypeTrait, RhsType: IntTypeTrait<Signed = LhsType::Signed>, Output: FixedOrDynIntType<1, Signed = ConstBool<false>>>"
 | 
			
		||||
        },
 | 
			
		||||
        "ops::CmpGt": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "ops::CmpGt::new_unchecked",
 | 
			
		||||
                "lhs": "Visible",
 | 
			
		||||
                "rhs": "Visible"
 | 
			
		||||
            },
 | 
			
		||||
            "generics": "<LhsType: IntTypeTrait, RhsType: IntTypeTrait<Signed = LhsType::Signed>, Output: FixedOrDynIntType<1, Signed = ConstBool<false>>>"
 | 
			
		||||
        },
 | 
			
		||||
        "ops::CmpGe": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "ops::CmpGe::new_unchecked",
 | 
			
		||||
                "lhs": "Visible",
 | 
			
		||||
                "rhs": "Visible"
 | 
			
		||||
            },
 | 
			
		||||
            "generics": "<LhsType: IntTypeTrait, RhsType: IntTypeTrait<Signed = LhsType::Signed>, Output: FixedOrDynIntType<1, Signed = ConstBool<false>>>"
 | 
			
		||||
        },
 | 
			
		||||
        "ops::CmpEq": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "ops::CmpEq::new_unchecked",
 | 
			
		||||
                "lhs": "Visible",
 | 
			
		||||
                "rhs": "Visible"
 | 
			
		||||
            },
 | 
			
		||||
            "generics": "<LhsType: IntTypeTrait, RhsType: IntTypeTrait<Signed = LhsType::Signed>, Output: FixedOrDynIntType<1, Signed = ConstBool<false>>>"
 | 
			
		||||
        },
 | 
			
		||||
        "ops::CmpNe": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "ops::CmpNe::new_unchecked",
 | 
			
		||||
                "lhs": "Visible",
 | 
			
		||||
                "rhs": "Visible"
 | 
			
		||||
            },
 | 
			
		||||
            "generics": "<LhsType: IntTypeTrait, RhsType: IntTypeTrait<Signed = LhsType::Signed>, Output: FixedOrDynIntType<1, Signed = ConstBool<false>>>"
 | 
			
		||||
        },
 | 
			
		||||
        "ops::CastInt": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "ops::CastInt::new_unchecked",
 | 
			
		||||
                "value": "Visible",
 | 
			
		||||
                "ty()": "Visible"
 | 
			
		||||
            },
 | 
			
		||||
            "generics": "<FromType: IntTypeTrait, ToType: IntTypeTrait>",
 | 
			
		||||
            "fold_where": "ToType: Fold<State>",
 | 
			
		||||
            "visit_where": "ToType: Visit<State>"
 | 
			
		||||
        },
 | 
			
		||||
        "ops::Slice": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "ops::Slice::new_unchecked",
 | 
			
		||||
                "base": "Visible",
 | 
			
		||||
                "range": "Opaque"
 | 
			
		||||
            },
 | 
			
		||||
            "generics": "<Base: IntTypeTrait>"
 | 
			
		||||
        },
 | 
			
		||||
        "ops::ReduceBitAnd": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "ops::ReduceBitAnd::new_unchecked",
 | 
			
		||||
                "arg": "Visible"
 | 
			
		||||
            },
 | 
			
		||||
            "generics": "<Output: FixedOrDynIntType<1, Signed = ConstBool<false>>>"
 | 
			
		||||
        },
 | 
			
		||||
        "ops::ReduceBitOr": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "ops::ReduceBitOr::new_unchecked",
 | 
			
		||||
                "arg": "Visible"
 | 
			
		||||
            },
 | 
			
		||||
            "generics": "<Output: FixedOrDynIntType<1, Signed = ConstBool<false>>>"
 | 
			
		||||
        },
 | 
			
		||||
        "ops::ReduceBitXor": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "ops::ReduceBitXor::new_unchecked",
 | 
			
		||||
                "arg": "Visible"
 | 
			
		||||
            },
 | 
			
		||||
            "generics": "<Output: FixedOrDynIntType<1, Signed = ConstBool<false>>>"
 | 
			
		||||
        },
 | 
			
		||||
        "ops::FieldAccess": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "ops::FieldAccess::new_unchecked",
 | 
			
		||||
                "base()": "Visible",
 | 
			
		||||
                "name()": "Visible"
 | 
			
		||||
            },
 | 
			
		||||
            "generics": {
 | 
			
		||||
                "generics": "<Base, Field>",
 | 
			
		||||
                "where": "Base: BundleType, Base::Value: BundleValue, Field: Type"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "ops::VariantAccess": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "ops::VariantAccess::new_unchecked",
 | 
			
		||||
                "base()": "Visible",
 | 
			
		||||
                "variant_index()": "Visible"
 | 
			
		||||
            },
 | 
			
		||||
            "generics": {
 | 
			
		||||
                "generics": "<T, VariantTy>",
 | 
			
		||||
                "where": "T: EnumType, T::Value: EnumValue, VariantTy: Type"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "ops::ArrayIndex": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "ops::ArrayIndex::new_unchecked",
 | 
			
		||||
                "base()": "Visible",
 | 
			
		||||
                "index()": "Visible"
 | 
			
		||||
            },
 | 
			
		||||
            "generics": "<ElementType: Type>"
 | 
			
		||||
        },
 | 
			
		||||
        "ops::DynArrayIndex": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "ops::DynArrayIndex::new_unchecked",
 | 
			
		||||
                "base()": "Visible",
 | 
			
		||||
                "index()": "Visible"
 | 
			
		||||
            },
 | 
			
		||||
            "generics": "<ElementType: Type>"
 | 
			
		||||
        },
 | 
			
		||||
        "ops::CastToBits": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "ops::CastToBits::new_unchecked",
 | 
			
		||||
                "value()": "Visible"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "ops::CastBitsTo": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "ops::CastBitsTo::new_unchecked",
 | 
			
		||||
                "value()": "Visible",
 | 
			
		||||
                "ty()": "Visible"
 | 
			
		||||
            },
 | 
			
		||||
            "generics": {
 | 
			
		||||
                "generics": "<T>",
 | 
			
		||||
                "where": "T: Type, T::Value: Value<Type = T>"
 | 
			
		||||
            },
 | 
			
		||||
            "fold_where": "T: Fold<State>",
 | 
			
		||||
            "visit_where": "T: Visit<State>"
 | 
			
		||||
        },
 | 
			
		||||
        "ops::CastBitToClock": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "ops::CastBitToClock::new_unchecked",
 | 
			
		||||
                "value": "Visible"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "ops::CastBitToSyncReset": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "ops::CastBitToSyncReset::new_unchecked",
 | 
			
		||||
                "value": "Visible"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "ops::CastBitToAsyncReset": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "ops::CastBitToAsyncReset::new_unchecked",
 | 
			
		||||
                "value": "Visible"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "ops::CastSyncResetToReset": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "ops::CastSyncResetToReset::new_unchecked",
 | 
			
		||||
                "value": "Visible"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "ops::CastAsyncResetToReset": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "ops::CastAsyncResetToReset::new_unchecked",
 | 
			
		||||
                "value": "Visible"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "ops::CastClockToBit": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "ops::CastClockToBit::new_unchecked",
 | 
			
		||||
                "value": "Visible"
 | 
			
		||||
            },
 | 
			
		||||
            "generics": "<ToType: FixedOrDynIntType<1>>"
 | 
			
		||||
        },
 | 
			
		||||
        "ops::CastSyncResetToBit": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "ops::CastSyncResetToBit::new_unchecked",
 | 
			
		||||
                "value": "Visible"
 | 
			
		||||
            },
 | 
			
		||||
            "generics": "<ToType: FixedOrDynIntType<1>>"
 | 
			
		||||
        },
 | 
			
		||||
        "ops::CastAsyncResetToBit": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "ops::CastAsyncResetToBit::new_unchecked",
 | 
			
		||||
                "value": "Visible"
 | 
			
		||||
            },
 | 
			
		||||
            "generics": "<ToType: FixedOrDynIntType<1>>"
 | 
			
		||||
        },
 | 
			
		||||
        "ops::CastResetToBit": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "ops::CastResetToBit::new_unchecked",
 | 
			
		||||
                "value": "Visible"
 | 
			
		||||
            },
 | 
			
		||||
            "generics": "<ToType: FixedOrDynIntType<1>>"
 | 
			
		||||
        },
 | 
			
		||||
        "BlockId": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Opaque"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "ReadUnderWrite": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Opaque"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "Instance": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "Instance::new_unchecked",
 | 
			
		||||
                "scoped_name()": "Visible",
 | 
			
		||||
                "instantiated()": "Visible",
 | 
			
		||||
                "source_location()": "Visible"
 | 
			
		||||
            },
 | 
			
		||||
            "generics": {
 | 
			
		||||
                "generics": "<T: BundleValue>",
 | 
			
		||||
                "where": "T::Type: BundleType<Value = T>"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "Reg": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "Reg::new_unchecked",
 | 
			
		||||
                "scoped_name()": "Visible",
 | 
			
		||||
                "source_location()": "Visible",
 | 
			
		||||
                "ty()": "Visible",
 | 
			
		||||
                "clock_domain()": "Visible",
 | 
			
		||||
                "init()": "Visible"
 | 
			
		||||
            },
 | 
			
		||||
            "generics": "<T: Type>",
 | 
			
		||||
            "fold_where": "T: Fold<State>",
 | 
			
		||||
            "visit_where": "T: Visit<State>"
 | 
			
		||||
        },
 | 
			
		||||
        "Wire": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "Wire::new_unchecked",
 | 
			
		||||
                "scoped_name()": "Visible",
 | 
			
		||||
                "source_location()": "Visible",
 | 
			
		||||
                "ty()": "Visible"
 | 
			
		||||
            },
 | 
			
		||||
            "generics": "<T: Type>",
 | 
			
		||||
            "fold_where": "T: Fold<State>",
 | 
			
		||||
            "visit_where": "T: Visit<State>"
 | 
			
		||||
        },
 | 
			
		||||
        "Stmt": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Enum",
 | 
			
		||||
                "Connect": "Visible",
 | 
			
		||||
                "If": "Visible",
 | 
			
		||||
                "Match": "Visible",
 | 
			
		||||
                "Declaration": "Visible"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "StmtDeclaration": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Enum",
 | 
			
		||||
                "Wire": "Visible",
 | 
			
		||||
                "Reg": "Visible",
 | 
			
		||||
                "Instance": "Visible"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "StmtConnect": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "lhs": "Visible",
 | 
			
		||||
                "rhs": "Visible",
 | 
			
		||||
                "source_location": "Visible"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "StmtIf": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "cond": "Visible",
 | 
			
		||||
                "source_location": "Visible",
 | 
			
		||||
                "blocks": "Visible"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "StmtInstance": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "annotations": "Visible",
 | 
			
		||||
                "instance": "Visible"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "StmtMatch": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "expr": "Visible",
 | 
			
		||||
                "source_location": "Visible",
 | 
			
		||||
                "blocks": "Visible"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "StmtReg": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "annotations": "Visible",
 | 
			
		||||
                "reg": "Visible"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "StmtWire": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "annotations": "Visible",
 | 
			
		||||
                "wire": "Visible"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "AnnotatedModuleIO": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "annotations": "Visible",
 | 
			
		||||
                "module_io": "Visible"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "TargetedAnnotation": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "TargetedAnnotation::new",
 | 
			
		||||
                "target()": "Visible",
 | 
			
		||||
                "annotation()": "RefVisible"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "Annotation": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Enum",
 | 
			
		||||
                "DontTouch": null,
 | 
			
		||||
                "CustomFirrtl": "Visible"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "CustomFirrtlAnnotation": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Opaque"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "Target": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Enum",
 | 
			
		||||
                "Base": "Visible",
 | 
			
		||||
                "Child": "Visible"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "TargetBase": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Enum",
 | 
			
		||||
                "ModuleIO": "Visible",
 | 
			
		||||
                "MemPort": "Visible",
 | 
			
		||||
                "Reg": "Visible",
 | 
			
		||||
                "Wire": "Visible",
 | 
			
		||||
                "Instance": "Visible"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "TargetChild": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "$constructor": "TargetChild::new",
 | 
			
		||||
                "parent()": "Visible",
 | 
			
		||||
                "path_element()": "Visible"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "TargetPathBundleField": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "name": "Visible"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "TargetPathArrayElement": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct",
 | 
			
		||||
                "index": "Visible"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "TargetPathDynArrayElement": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Struct"
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "TargetPathElement": {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "$kind": "Enum",
 | 
			
		||||
                "BundleField": "Visible",
 | 
			
		||||
                "ArrayElement": "Visible",
 | 
			
		||||
                "DynArrayElement": "Visible"
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue