diff --git a/Cargo.lock b/Cargo.lock index bea172b0..2332be33 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -317,7 +317,7 @@ dependencies = [ [[package]] name = "pyqir" -version = "0.10.5" +version = "0.10.6" dependencies = [ "const-str", "llvm-sys 110.0.4", @@ -331,7 +331,7 @@ dependencies = [ [[package]] name = "qirlib" -version = "0.10.5" +version = "0.10.6" dependencies = [ "bitvec", "cc", diff --git a/Cargo.toml b/Cargo.toml index 4b293ae4..3e6f9904 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ resolver = "2" [workspace.package] authors = ["Microsoft"] -version = "0.10.5" +version = "0.10.6" edition = "2021" license = "MIT" homepage = "https://github.com/qir-alliance/pyqir" diff --git a/pyqir/NOTICE-WHEEL.txt b/pyqir/NOTICE-WHEEL.txt index 5c6cd936..2059c050 100644 --- a/pyqir/NOTICE-WHEEL.txt +++ b/pyqir/NOTICE-WHEEL.txt @@ -5595,7 +5595,7 @@ limitations under the License. -qirlib 0.10.5 - SPDX: MIT - MIT License +qirlib 0.10.6 - SPDX: MIT - MIT License https://github.com/qir-alliance/pyqir /* @@ -5970,7 +5970,7 @@ SOFTWARE. -pyqir 0.10.5 - SPDX: MIT - MIT License +pyqir 0.10.6 - SPDX: MIT - MIT License https://github.com/qir-alliance/pyqir MIT License diff --git a/pyqir/pyproject.toml b/pyqir/pyproject.toml index f0ed8d95..5166df51 100644 --- a/pyqir/pyproject.toml +++ b/pyqir/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "pyqir" -version = "0.10.5" +version = "0.10.6" requires-python = ">= 3.8" classifiers = [ "License :: OSI Approved :: MIT License", diff --git a/pyqir/pyqir/_native.pyi b/pyqir/pyqir/_native.pyi index 4b151057..6c93fa9d 100644 --- a/pyqir/pyqir/_native.pyi +++ b/pyqir/pyqir/_native.pyi @@ -925,6 +925,11 @@ class Value: """The name of this value or the empty string if this value is anonymous.""" ... + @name.setter + def name(self, value: str) -> None: + """Sets the name of the value.""" + ... + def __richcmp__(self, other: Value, op: int) -> bool: """ Compares this value to another value. diff --git a/pyqir/src/values.rs b/pyqir/src/values.rs index c9de753f..e9e6c52f 100644 --- a/pyqir/src/values.rs +++ b/pyqir/src/values.rs @@ -93,6 +93,18 @@ impl Value { } } + #[setter] + fn set_name(&self, value: &str) { + unsafe { + let c_name = &CString::new(value).unwrap(); + LLVMSetValueName2( + self.as_ptr(), + value.as_ptr().cast(), + c_name.as_bytes().len(), + ); + } + } + fn __str__(&self) -> String { unsafe { Message::from_raw(LLVMPrintValueToString(self.as_ptr())) diff --git a/pyqir/tests/test_value_name.py b/pyqir/tests/test_value_name.py new file mode 100644 index 00000000..b7a51d4d --- /dev/null +++ b/pyqir/tests/test_value_name.py @@ -0,0 +1,81 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +from pathlib import Path + +import pyqir + + +current_file_path = Path(__file__) +# Get the directory of the current file +current_dir = current_file_path.parent + +from pyqir import ( + Context, + is_entry_point, +) + + +def read_file(file_name: str) -> str: + return Path(current_dir / file_name).read_text(encoding="utf-8") + + +def test_setting_function_name_changes_name() -> None: + context = Context() + ir = read_file("random_bit.ll") + mod = pyqir.Module.from_ir(context, ir) + entry_point = next(filter(is_entry_point, mod.functions)) + assert entry_point.name == "random_bit" + expected = "new_name" + entry_point.name = expected + entry_point = next(filter(is_entry_point, mod.functions)) + assert entry_point.name == expected + + +def test_setting_constant_name_does_not_do_anything() -> None: + context = pyqir.Context() + const0 = pyqir.const(pyqir.IntType(context, 64), 42) + const0.name = "my_value" + assert const0.name == "" + + +def test_setting_block_name_changes_name() -> None: + mod = pyqir.SimpleModule("test_type_mismatch", 0, 0) + mod.entry_block.name = "my_block" + ir = mod.ir() + assert "my_block:" in ir + + +def test_int_variable() -> None: + mod = pyqir.SimpleModule("test", 0, 0) + i64 = pyqir.IntType(mod.context, 64) + + source = mod.add_external_function("source", pyqir.FunctionType(i64, [])) + sink = mod.add_external_function("sink", pyqir.FunctionType(i64, [i64])) + + source_res = mod.builder.call(source, []) + source_res.name = "my_var" + + sink_res = mod.builder.call(sink, [source_res]) + sink_res.name = "my_res" + ir = mod.ir() + assert "%my_var = call i64 @source()" in ir + assert "%my_res = call i64 @sink(i64 %my_var)" in ir + + +def test_function_name_can_contain_spaces_and_chars() -> None: + simple_mod = pyqir.SimpleModule("test", 0, 0) + expected = "Some - ; name fin" + simple_mod.entry_point.name = expected + + # verify the name is use and wrapped in quotes + ir = simple_mod.ir() + assert f'@"{expected}"() #0' in ir + + # verify we can find it by name without having to use quotes + func = next(filter(lambda f: f.name == expected, simple_mod._module.functions)) + assert func == simple_mod.entry_point + + # Double check that the module is valid with this kind of name + mod = pyqir.Module.from_ir(Context(), ir) + assert mod.verify() is None