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