Mercurial > public > mercurial-scm > hg
comparison rust/hg-cpython/src/revlog.rs @ 51252:24d3298189d7
rust-index: document safety invariants being upheld for every `unsafe` block
We've added a lot of `unsafe` code that shares Rust structs with Python.
While this is unfortunate, it is also unavoidable, so let's at least
systematically explain why each call to `unsafe` is sound.
If any of the unsafe code ends up being wrong (because everyone screws up
at some point), this change at least continues the unspoken rule of always
explaining the need for `unsafe`, so we at least get a chance to think.
author | Rapha?l Gom?s <rgomes@octobus.net> |
---|---|
date | Thu, 23 Nov 2023 03:41:58 +0100 |
parents | f94c10334bcb |
children | 8b89f7cc953a |
comparison
equal
deleted
inserted
replaced
51251:f94c10334bcb | 51252:24d3298189d7 |
---|---|
40 py: Python, | 40 py: Python, |
41 index: PyObject, | 41 index: PyObject, |
42 ) -> PyResult<UnsafePyLeaked<PySharedIndex>> { | 42 ) -> PyResult<UnsafePyLeaked<PySharedIndex>> { |
43 let midx = index.extract::<Index>(py)?; | 43 let midx = index.extract::<Index>(py)?; |
44 let leaked = midx.index(py).leak_immutable(); | 44 let leaked = midx.index(py).leak_immutable(); |
45 // Safety: we don't leak the "faked" reference out of the `UnsafePyLeaked` | |
45 Ok(unsafe { leaked.map(py, |idx| PySharedIndex { inner: idx }) }) | 46 Ok(unsafe { leaked.map(py, |idx| PySharedIndex { inner: idx }) }) |
46 } | 47 } |
47 | 48 |
48 impl Clone for PySharedIndex { | 49 impl Clone for PySharedIndex { |
49 fn clone(&self) -> Self { | 50 fn clone(&self) -> Self { |
973 /// the inner index would fail because of `PySharedRef` poisoning | 974 /// the inner index would fail because of `PySharedRef` poisoning |
974 /// (generation-based guard), same as iterating on a `dict` that has | 975 /// (generation-based guard), same as iterating on a `dict` that has |
975 /// been meanwhile mutated. | 976 /// been meanwhile mutated. |
976 def is_invalidated(&self) -> PyResult<bool> { | 977 def is_invalidated(&self) -> PyResult<bool> { |
977 let leaked = self.index(py).borrow(); | 978 let leaked = self.index(py).borrow(); |
979 // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked` | |
978 let result = unsafe { leaked.try_borrow(py) }; | 980 let result = unsafe { leaked.try_borrow(py) }; |
979 // two cases for result to be an error: | 981 // two cases for result to be an error: |
980 // - the index has previously been mutably borrowed | 982 // - the index has previously been mutably borrowed |
981 // - there is currently a mutable borrow | 983 // - there is currently a mutable borrow |
982 // in both cases this amounts for previous results related to | 984 // in both cases this amounts for previous results related to |
984 Ok(result.is_err()) | 986 Ok(result.is_err()) |
985 } | 987 } |
986 | 988 |
987 def insert(&self, rev: PyRevision) -> PyResult<PyObject> { | 989 def insert(&self, rev: PyRevision) -> PyResult<PyObject> { |
988 let leaked = self.index(py).borrow(); | 990 let leaked = self.index(py).borrow(); |
991 // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked` | |
989 let index = &*unsafe { leaked.try_borrow(py)? }; | 992 let index = &*unsafe { leaked.try_borrow(py)? }; |
990 | 993 |
991 let rev = UncheckedRevision(rev.0); | 994 let rev = UncheckedRevision(rev.0); |
992 let rev = index | 995 let rev = index |
993 .check_revision(rev) | 996 .check_revision(rev) |
1018 node_prefix.as_object())) | 1021 node_prefix.as_object())) |
1019 )?; | 1022 )?; |
1020 | 1023 |
1021 let nt = self.nt(py).borrow(); | 1024 let nt = self.nt(py).borrow(); |
1022 let leaked = self.index(py).borrow(); | 1025 let leaked = self.index(py).borrow(); |
1026 // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked` | |
1023 let index = &*unsafe { leaked.try_borrow(py)? }; | 1027 let index = &*unsafe { leaked.try_borrow(py)? }; |
1024 | 1028 |
1025 Ok(nt.find_bin(index, prefix) | 1029 Ok(nt.find_bin(index, prefix) |
1026 .map_err(|e| nodemap_error(py, e))? | 1030 .map_err(|e| nodemap_error(py, e))? |
1027 .map(|r| r.into()) | 1031 .map(|r| r.into()) |
1029 } | 1033 } |
1030 | 1034 |
1031 def shortest(&self, node: PyBytes) -> PyResult<usize> { | 1035 def shortest(&self, node: PyBytes) -> PyResult<usize> { |
1032 let nt = self.nt(py).borrow(); | 1036 let nt = self.nt(py).borrow(); |
1033 let leaked = self.index(py).borrow(); | 1037 let leaked = self.index(py).borrow(); |
1038 // Safety: we don't leak the "faked" reference out of `UnsafePyLeaked` | |
1034 let idx = &*unsafe { leaked.try_borrow(py)? }; | 1039 let idx = &*unsafe { leaked.try_borrow(py)? }; |
1035 match nt.unique_prefix_len_node(idx, &node_from_py_bytes(py, &node)?) | 1040 match nt.unique_prefix_len_node(idx, &node_from_py_bytes(py, &node)?) |
1036 { | 1041 { |
1037 Ok(Some(l)) => Ok(l), | 1042 Ok(Some(l)) => Ok(l), |
1038 Ok(None) => Err(revlog_error(py)), | 1043 Ok(None) => Err(revlog_error(py)), |