@@ -47,13 +47,17 @@ inline void decref(PyObject *) noexcept;
4747template <>
4848inline void decref<false >(PyObject *obj) noexcept
4949{
50+ // Assert that the reference is valid if it is not null.
51+ PYDYND_ASSERT_IF (obj != nullptr , Py_REFCNT (obj) > 0 );
5052 Py_XDECREF (obj);
5153}
5254
5355template <>
5456inline void decref<true >(PyObject *obj) noexcept
5557{
5658 assert (obj != nullptr );
59+ // Assert that the reference is valid.
60+ assert (Py_REFCNT (obj) > 0 );
5761 Py_DECREF (obj);
5862}
5963
@@ -90,23 +94,33 @@ template <>
9094inline void decref_if_owned<true , true >(PyObject *obj) noexcept
9195{
9296 assert (obj != nullptr );
97+ // Assert that the reference is valid.
98+ assert (Py_REFCNT (obj) > 0 );
9399 Py_DECREF (obj);
94100}
95101
96102template <>
97103inline void decref_if_owned<true , false >(PyObject *obj) noexcept
98104{
105+ // Assert that the reference is valid if it is not null.
106+ PYDYND_ASSERT_IF (obj != nullptr , Py_REFCNT (obj) > 0 );
99107 Py_XDECREF (obj);
100108}
101109
110+ // This is a no-op in non-debug builds.
111+ // In debug builds, it asserts that the reference is actually valid.
102112template <>
103113inline void decref_if_owned<false , true >(PyObject *obj) noexcept
104114{
115+ assert (Py_REFCNT (obj) > 0 );
105116}
106117
118+ // This is a no-op in non-debug builds.
119+ // In debug builds, it asserts that the reference is actually valid.
107120template <>
108121inline void decref_if_owned<false , false >(PyObject *obj) noexcept
109122{
123+ PYDYND_ASSERT_IF (obj != nullptr , Py_REFCNT (obj) > 0 );
110124}
111125
112126template <bool owns_ref = true , bool not_null = true >
@@ -125,6 +139,8 @@ class py_ref_tmpl {
125139 PyObject *get () noexcept
126140 {
127141 PYDYND_ASSERT_IF (not_null, o != nullptr );
142+ // Assert that the accessed reference is still valid.
143+ PYDYND_ASSERT_IF (o != nullptr , Py_REFCNT (o) > 0 );
128144 return o;
129145 }
130146
@@ -133,18 +149,18 @@ class py_ref_tmpl {
133149 // they are also declared noexcept.
134150
135151 /* If:
136- * this type allows null,
137- * Or:
138- * this type doesn't allow null
139- * and the input type doesn't allow null,
152+ * This type allows null or the input type does not,
140153 * Then:
141154 * Conversions from the other py_ref_tmpl type to this type do not raise exceptions.
142155 */
143156 template <bool other_owns_ref, bool other_not_null,
144- typename std::enable_if_t <( other_not_null && not_null) || !not_null> * = nullptr >
157+ typename std::enable_if_t <other_not_null || !not_null> * = nullptr >
145158 py_ref_tmpl (const py_ref_tmpl<other_owns_ref, other_not_null> &other) noexcept
146159 {
147- PYDYND_ASSERT_IF (not_null || other_not_null, other.o != nullptr );
160+ // If the input is not null, assert that it isn't.
161+ PYDYND_ASSERT_IF (other_not_null, other.o != nullptr );
162+ // Assert that the input reference is valid if it is not null.
163+ PYDYND_ASSERT_IF (other.o != nullptr , Py_REFCNT (other.o ) > 0 );
148164 o = other.o ;
149165 incref_if_owned<owns_ref, not_null>(o);
150166 }
@@ -162,6 +178,8 @@ class py_ref_tmpl {
162178 py_ref_tmpl (const py_ref_tmpl<other_owns_ref, other_not_null> &other)
163179 {
164180 if (other.o != nullptr ) {
181+ // Assert that the input reference is valid.
182+ assert (Py_REFCNT (other.o ) > 0 );
165183 o = other.o ;
166184 incref_if_owned<owns_ref, not_null>(o);
167185 }
@@ -180,22 +198,26 @@ class py_ref_tmpl {
180198 * Then:
181199 * a move operation is defined and will not raise an exception.
182200 */
183- template <bool other_not_null, typename std::enable_if_t <( other_not_null || !not_null) > * = nullptr >
201+ template <bool other_not_null, typename std::enable_if_t <other_not_null || !not_null> * = nullptr >
184202 py_ref_tmpl (py_ref_tmpl<true , other_not_null> &&other) noexcept
185203 {
186204 // If this type is a non-null type, the assigned value should not be null.
187205 // If the other type is a non-null type, the provided value should not be null,
188206 // unless it is being used uninitialized or after being moved from.
189207 PYDYND_ASSERT_IF (not_null || other_not_null, other.o != nullptr );
208+ // If the reference is not null, assert that it is valid.
209+ PYDYND_ASSERT_IF (other.o != nullptr , Py_REFCNT (other.o ) > 0 );
190210 // Use get to assert that other.o is not null if other_not_null is true.
191211 o = other.o ;
212+ // Make sure other can be destructed without decrefing
213+ // this object's wrapped pointer.
192214 other.o = nullptr ;
193215 // The other type owns its reference.
194216 // If this one does not, decref the new pointer.
195217 // If this type is a non-null type, the assigned value should not be null.
196218 decref_if_owned<!owns_ref, not_null>(o);
197- // Make sure other can be destructed without decrefing
198- // this object's wrapped pointer.
219+ // If the reference is not null, assert that it is still valid.
220+ PYDYND_ASSERT_IF (o != nullptr , Py_REFCNT (o) > 0 )
199221 }
200222
201223 /* If:
@@ -208,14 +230,18 @@ class py_ref_tmpl {
208230 py_ref_tmpl (py_ref_tmpl<true , other_not_null> &&other)
209231 {
210232 if (other.o != nullptr ) {
233+ // Assert that the input reference is valid.
234+ assert (Py_REFCNT (other.o ) > 0 );
211235 o = other.o ;
236+ // Make sure other can be destructed without decrefing
237+ // this object's wrapped pointer.
212238 other.o = nullptr ;
213239 // The other type owns its reference.
214240 // If this one does not, decref the new pointer.
215241 // The assigned value is already known not be null.
216242 decref_if_owned<!owns_ref, not_null>(o);
217- // Make sure other can be destructed without decrefing
218- // this object's wrapped pointer.
243+ // Assert that the stored reference is still valid
244+ assert ( Py_REFCNT (o) > 0 );
219245 }
220246 else {
221247 throw std::invalid_argument (" Cannot convert null valued pointer to non-null reference." );
@@ -239,6 +265,9 @@ class py_ref_tmpl {
239265 else {
240266 incref_if_owned<owns_ref, not_null>(o);
241267 }
268+ // Regardless of whether or not a reference was consumed,
269+ // assert that it is valid if it is not null.
270+ PYDYND_ASSERT_IF (obj != nullptr , Py_REFCNT (obj) > 0 );
242271 }
243272
244273 ~py_ref_tmpl ()
@@ -257,18 +286,18 @@ class py_ref_tmpl {
257286 // Assignment never comsumes a reference.
258287
259288 /* If:
260- * this type allows null,
261- * Or:
262- * this type doesn't allow null
263- * and the input type doesn't allow null,
289+ * This type allows null or the input type does not,
264290 * Then:
265291 * Assignment from the other py_ref_tmpl type to this type may not raise an exception.
266292 */
267293 template <bool other_owns_ref, bool other_not_null,
268- typename std::enable_if_t <( other_not_null && not_null) || !not_null> * = nullptr >
294+ typename std::enable_if_t <other_not_null || !not_null> * = nullptr >
269295 py_ref_tmpl<owns_ref, not_null> &operator =(const py_ref_tmpl<other_owns_ref, other_not_null> &other) noexcept
270296 {
271- PYDYND_ASSERT_IF (not_null || other_not_null, other.o != nullptr );
297+ // Assert that the input reference is not null if the input type does not allow nulls.
298+ PYDYND_ASSERT_IF (other_not_null, other.o != nullptr );
299+ // Assert that the input reference is valid if it is not null.
300+ PYDYND_ASSERT_IF (other.o != nullptr , Py_REFCNT (other.o ) > 0 );
272301 // Nullcheck when doing decref in case this object
273302 // is uninitialized or has been moved from.
274303 decref_if_owned<owns_ref, false >(o);
@@ -288,6 +317,8 @@ class py_ref_tmpl {
288317 py_ref_tmpl<owns_ref, not_null> &operator =(const py_ref_tmpl<other_owns_ref, other_not_null> &other) noexcept
289318 {
290319 if (other.o != nullptr ) {
320+ // Assert that the input reference is valid.
321+ assert (Py_REFCNT (other.o ) > 0 );
291322 // Nullcheck when doing decref in case this object
292323 // is uninitialized or has been moved from.
293324 decref_if_owned<owns_ref, false >(o);
@@ -311,10 +342,13 @@ class py_ref_tmpl {
311342 * Assignment from the other py_ref_tmpl type to this type may not raise an exception.
312343 */
313344 template <bool other_owns_ref, bool other_not_null,
314- typename std::enable_if_t <( other_not_null && not_null) || !not_null> * = nullptr >
345+ typename std::enable_if_t <other_not_null || !not_null> * = nullptr >
315346 py_ref_tmpl<owns_ref, not_null> &operator =(py_ref_tmpl<other_owns_ref, other_not_null> &&other) noexcept
316347 {
317- PYDYND_ASSERT_IF (not_null || other_not_null, other.o != nullptr );
348+ // If the input reference should not be null, assert that that is the case.
349+ PYDYND_ASSERT_IF (other_not_null, other.o != nullptr );
350+ // If the input reference is not null, assert that it is valid.
351+ PYDYND_ASSERT_IF (other.o != nullptr , Py_REFCNT (other.o ) > 0 );
318352 // Nullcheck when doing decref in case this object
319353 // is uninitialized or has been moved from.
320354 decref_if_owned<owns_ref, false >(o);
@@ -335,6 +369,8 @@ class py_ref_tmpl {
335369 py_ref_tmpl<owns_ref, not_null> &operator =(py_ref_tmpl<other_owns_ref, other_not_null> &&other) noexcept
336370 {
337371 if (other.o != nullptr ) {
372+ // Assert that the input reference is valid.
373+ assert (Py_REFCNT (other.o ) > 0 );
338374 // Nullcheck when doing decref in case this object
339375 // is uninitialized or has been moved from.
340376 decref_if_owned<owns_ref, false >(o);
@@ -352,16 +388,16 @@ class py_ref_tmpl {
352388 // Set the encapsulated pointer to NULL.
353389 PyObject *release () noexcept
354390 {
391+ // If the contained reference should not be null, assert that it isn't.
355392 PYDYND_ASSERT_IF (not_null, o != nullptr );
393+ // If the contained reference is not null, assert that it is valid.
394+ PYDYND_ASSERT_IF (o != nullptr , Py_REFCNT (o) > 0 );
356395 auto ret = o;
357396 o = nullptr ;
358397 incref_if_owned<!owns_ref, not_null>(ret);
359398 return ret;
360399 }
361400
362- // Check if the wrapped pointer is null.
363- bool is_null () noexcept { return o != nullptr ; }
364-
365401 py_ref_tmpl<false , not_null> borrow () noexcept { return py_ref<false , not_null>(o, false ); }
366402};
367403
@@ -383,9 +419,7 @@ inline py_ref capture_if_not_null(PyObject *o)
383419 if (o == nullptr ) {
384420 throw std::runtime_error (" Unexpected null pointer." );
385421 }
386- auto p = py_ref (o, true );
387- return p;
388- // return py_ref(o, true);
422+ return py_ref (o, true );
389423}
390424
391425/* Convert to a non-null reference.
@@ -408,7 +442,10 @@ inline py_ref_tmpl<owns_ref, true> nullcheck(py_ref_tmpl<owns_ref, not_null> &&o
408442template <bool owns_ref, bool not_null>
409443inline py_ref_tmpl<owns_ref, true > disallow_null (py_ref_tmpl<owns_ref, not_null> &&obj) noexcept
410444{
445+ // Assert that the wrapped pointer is actually not null.
411446 assert (obj.get () != nullptr );
447+ // Assert that the wrapped reference is valid if it is not null.
448+ PYDYND_ASSERT_IF (obj.get () != nullptr , Py_REFCNT (obj.get ()) > 0 );
412449 return py_ref_tmpl<owns_ref, true >(obj.release (), owns_ref);
413450}
414451
0 commit comments