python-peps/pep-3123/index.html

263 lines
18 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="color-scheme" content="light dark">
<title>PEP 3123 Making PyObject_HEAD conform to standard C | peps.python.org</title>
<link rel="shortcut icon" href="../_static/py.png">
<link rel="canonical" href="https://peps.python.org/pep-3123/">
<link rel="stylesheet" href="../_static/style.css" type="text/css">
<link rel="stylesheet" href="../_static/mq.css" type="text/css">
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" media="(prefers-color-scheme: light)" id="pyg-light">
<link rel="stylesheet" href="../_static/pygments_dark.css" type="text/css" media="(prefers-color-scheme: dark)" id="pyg-dark">
<link rel="alternate" type="application/rss+xml" title="Latest PEPs" href="https://peps.python.org/peps.rss">
<meta property="og:title" content='PEP 3123 Making PyObject_HEAD conform to standard C | peps.python.org'>
<meta property="og:description" content="Python currently relies on undefined C behavior, with its usage of PyObject_HEAD. This PEP proposes to change that into standard C.">
<meta property="og:type" content="website">
<meta property="og:url" content="https://peps.python.org/pep-3123/">
<meta property="og:site_name" content="Python Enhancement Proposals (PEPs)">
<meta property="og:image" content="https://peps.python.org/_static/og-image.png">
<meta property="og:image:alt" content="Python PEPs">
<meta property="og:image:width" content="200">
<meta property="og:image:height" content="200">
<meta name="description" content="Python currently relies on undefined C behavior, with its usage of PyObject_HEAD. This PEP proposes to change that into standard C.">
<meta name="theme-color" content="#3776ab">
</head>
<body>
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
<symbol id="svg-sun-half" viewBox="0 0 24 24" pointer-events="all">
<title>Following system colour scheme</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="9"></circle>
<path d="M12 3v18m0-12l4.65-4.65M12 14.3l7.37-7.37M12 19.6l8.85-8.85"></path>
</svg>
</symbol>
<symbol id="svg-moon" viewBox="0 0 24 24" pointer-events="all">
<title>Selected dark colour scheme</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z"></path>
</svg>
</symbol>
<symbol id="svg-sun" viewBox="0 0 24 24" pointer-events="all">
<title>Selected light colour scheme</title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="5"></circle>
<line x1="12" y1="1" x2="12" y2="3"></line>
<line x1="12" y1="21" x2="12" y2="23"></line>
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
<line x1="1" y1="12" x2="3" y2="12"></line>
<line x1="21" y1="12" x2="23" y2="12"></line>
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
</svg>
</symbol>
</svg>
<script>
document.documentElement.dataset.colour_scheme = localStorage.getItem("colour_scheme") || "auto"
</script>
<section id="pep-page-section">
<header>
<h1>Python Enhancement Proposals</h1>
<ul class="breadcrumbs">
<li><a href="https://www.python.org/" title="The Python Programming Language">Python</a> &raquo; </li>
<li><a href="../pep-0000/">PEP Index</a> &raquo; </li>
<li>PEP 3123</li>
</ul>
<button id="colour-scheme-cycler" onClick="setColourScheme(nextColourScheme())">
<svg aria-hidden="true" class="colour-scheme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
<svg aria-hidden="true" class="colour-scheme-icon-when-dark"><use href="#svg-moon"></use></svg>
<svg aria-hidden="true" class="colour-scheme-icon-when-light"><use href="#svg-sun"></use></svg>
<span class="visually-hidden">Toggle light / dark / auto colour theme</span>
</button>
</header>
<article>
<section id="pep-content">
<h1 class="page-title">PEP 3123 Making PyObject_HEAD conform to standard C</h1>
<dl class="rfc2822 field-list simple">
<dt class="field-odd">Author<span class="colon">:</span></dt>
<dd class="field-odd">Martin von Löwis &lt;martin&#32;&#97;t&#32;v.loewis.de&gt;</dd>
<dt class="field-even">Status<span class="colon">:</span></dt>
<dd class="field-even"><abbr title="Accepted and implementation complete, or no longer active">Final</abbr></dd>
<dt class="field-odd">Type<span class="colon">:</span></dt>
<dd class="field-odd"><abbr title="Normative PEP with a new feature for Python, implementation change for CPython or interoperability standard for the ecosystem">Standards Track</abbr></dd>
<dt class="field-even">Created<span class="colon">:</span></dt>
<dd class="field-even">27-Apr-2007</dd>
<dt class="field-odd">Python-Version<span class="colon">:</span></dt>
<dd class="field-odd">3.0</dd>
<dt class="field-even">Post-History<span class="colon">:</span></dt>
<dd class="field-even"><p></p></dd>
</dl>
<hr class="docutils" />
<section id="contents">
<details><summary>Table of Contents</summary><ul class="simple">
<li><a class="reference internal" href="#abstract">Abstract</a></li>
<li><a class="reference internal" href="#rationale">Rationale</a></li>
<li><a class="reference internal" href="#specification">Specification</a></li>
<li><a class="reference internal" href="#compatibility-with-python-2-6">Compatibility with Python 2.6</a></li>
<li><a class="reference internal" href="#copyright">Copyright</a></li>
</ul>
</details></section>
<section id="abstract">
<h2><a class="toc-backref" href="#abstract" role="doc-backlink">Abstract</a></h2>
<p>Python currently relies on undefined C behavior, with its
usage of <code class="docutils literal notranslate"><span class="pre">PyObject_HEAD</span></code>. This PEP proposes to change that
into standard C.</p>
</section>
<section id="rationale">
<h2><a class="toc-backref" href="#rationale" role="doc-backlink">Rationale</a></h2>
<p>Standard C defines that an object must be accessed only through a
pointer of its type, and that all other accesses are undefined
behavior, with a few exceptions. In particular, the following
code has undefined behavior:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">struct</span> <span class="n">FooObject</span><span class="p">{</span>
<span class="n">PyObject_HEAD</span>
<span class="nb">int</span> <span class="n">data</span><span class="p">;</span>
<span class="p">};</span>
<span class="n">PyObject</span> <span class="o">*</span><span class="n">foo</span><span class="p">(</span><span class="n">struct</span> <span class="n">FooObject</span><span class="o">*</span><span class="n">f</span><span class="p">){</span>
<span class="k">return</span> <span class="p">(</span><span class="n">PyObject</span><span class="o">*</span><span class="p">)</span><span class="n">f</span><span class="p">;</span>
<span class="p">}</span>
<span class="nb">int</span> <span class="n">bar</span><span class="p">(){</span>
<span class="n">struct</span> <span class="n">FooObject</span> <span class="o">*</span><span class="n">f</span> <span class="o">=</span> <span class="n">malloc</span><span class="p">(</span><span class="n">sizeof</span><span class="p">(</span><span class="n">struct</span> <span class="n">FooObject</span><span class="p">));</span>
<span class="n">struct</span> <span class="n">PyObject</span> <span class="o">*</span><span class="n">o</span> <span class="o">=</span> <span class="n">foo</span><span class="p">(</span><span class="n">f</span><span class="p">);</span>
<span class="n">f</span><span class="o">-&gt;</span><span class="n">ob_refcnt</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="n">o</span><span class="o">-&gt;</span><span class="n">ob_refcnt</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="k">return</span> <span class="n">f</span><span class="o">-&gt;</span><span class="n">ob_refcnt</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
</div>
<p>The problem here is that the storage is both accessed as
if it where struct <code class="docutils literal notranslate"><span class="pre">PyObject</span></code>, and as struct <code class="docutils literal notranslate"><span class="pre">FooObject</span></code>.</p>
<p>Historically, compilers did not have any problems with this
code. However, modern compilers use that clause as an
optimization opportunity, finding that <code class="docutils literal notranslate"><span class="pre">f-&gt;ob_refcnt</span></code> and
<code class="docutils literal notranslate"><span class="pre">o-&gt;ob_refcnt</span></code> cannot possibly refer to the same memory, and
that therefore the function should return 0, without having
to fetch the value of ob_refcnt at all in the return
statement. For GCC, Python now uses <code class="docutils literal notranslate"><span class="pre">-fno-strict-aliasing</span></code>
to work around that problem; with other compilers, it
may just see undefined behavior. Even with GCC, using
<code class="docutils literal notranslate"><span class="pre">-fno-strict-aliasing</span></code> may pessimize the generated code
unnecessarily.</p>
</section>
<section id="specification">
<h2><a class="toc-backref" href="#specification" role="doc-backlink">Specification</a></h2>
<p>Standard C has one specific exception to its aliasing rules precisely
designed to support the case of Python: a value of a struct type may
also be accessed through a pointer to the first field. E.g. if a
struct starts with an <code class="docutils literal notranslate"><span class="pre">int</span></code>, the <code class="docutils literal notranslate"><span class="pre">struct</span> <span class="pre">*</span></code> may also be cast to
an <code class="docutils literal notranslate"><span class="pre">int</span> <span class="pre">*</span></code>, allowing to write int values into the first field.</p>
<p>For Python, <code class="docutils literal notranslate"><span class="pre">PyObject_HEAD</span></code> and <code class="docutils literal notranslate"><span class="pre">PyObject_VAR_HEAD</span></code> will be changed
to not list all fields anymore, but list a single field of type
<code class="docutils literal notranslate"><span class="pre">PyObject</span></code>/<code class="docutils literal notranslate"><span class="pre">PyVarObject</span></code>:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">typedef</span> <span class="n">struct</span> <span class="n">_object</span> <span class="p">{</span>
<span class="n">_PyObject_HEAD_EXTRA</span>
<span class="n">Py_ssize_t</span> <span class="n">ob_refcnt</span><span class="p">;</span>
<span class="n">struct</span> <span class="n">_typeobject</span> <span class="o">*</span><span class="n">ob_type</span><span class="p">;</span>
<span class="p">}</span> <span class="n">PyObject</span><span class="p">;</span>
<span class="n">typedef</span> <span class="n">struct</span> <span class="p">{</span>
<span class="n">PyObject</span> <span class="n">ob_base</span><span class="p">;</span>
<span class="n">Py_ssize_t</span> <span class="n">ob_size</span><span class="p">;</span>
<span class="p">}</span> <span class="n">PyVarObject</span><span class="p">;</span>
<span class="c1">#define PyObject_HEAD PyObject ob_base;</span>
<span class="c1">#define PyObject_VAR_HEAD PyVarObject ob_base;</span>
</pre></div>
</div>
<p>Types defined as fixed-size structure will then include PyObject
as its first field, PyVarObject for variable-sized objects. E.g.:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">typedef</span> <span class="n">struct</span> <span class="p">{</span>
<span class="n">PyObject</span> <span class="n">ob_base</span><span class="p">;</span>
<span class="n">PyObject</span> <span class="o">*</span><span class="n">start</span><span class="p">,</span> <span class="o">*</span><span class="n">stop</span><span class="p">,</span> <span class="o">*</span><span class="n">step</span><span class="p">;</span>
<span class="p">}</span> <span class="n">PySliceObject</span><span class="p">;</span>
<span class="n">typedef</span> <span class="n">struct</span> <span class="p">{</span>
<span class="n">PyVarObject</span> <span class="n">ob_base</span><span class="p">;</span>
<span class="n">PyObject</span> <span class="o">**</span><span class="n">ob_item</span><span class="p">;</span>
<span class="n">Py_ssize_t</span> <span class="n">allocated</span><span class="p">;</span>
<span class="p">}</span> <span class="n">PyListObject</span><span class="p">;</span>
</pre></div>
</div>
<p>The above definitions of <code class="docutils literal notranslate"><span class="pre">PyObject_HEAD</span></code> are normative, so extension
authors MAY either use the macro, or put the <code class="docutils literal notranslate"><span class="pre">ob_base</span></code> field explicitly
into their structs.</p>
<p>As a convention, the base field SHOULD be called ob_base. However, all
accesses to ob_refcnt and ob_type MUST cast the object pointer to
PyObject* (unless the pointer is already known to have that type), and
SHOULD use the respective accessor macros. To simplify access to
ob_type, ob_refcnt, and ob_size, macros:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1">#define Py_TYPE(o) (((PyObject*)(o))-&gt;ob_type)</span>
<span class="c1">#define Py_REFCNT(o) (((PyObject*)(o))-&gt;ob_refcnt)</span>
<span class="c1">#define Py_SIZE(o) (((PyVarObject*)(o))-&gt;ob_size)</span>
</pre></div>
</div>
<p>are added. E.g. the code blocks</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1">#define PyList_CheckExact(op) ((op)-&gt;ob_type == &amp;PyList_Type)</span>
<span class="k">return</span> <span class="n">func</span><span class="o">-&gt;</span><span class="n">ob_type</span><span class="o">-&gt;</span><span class="n">tp_name</span><span class="p">;</span>
</pre></div>
</div>
<p>needs to be changed to:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="c1">#define PyList_CheckExact(op) (Py_TYPE(op) == &amp;PyList_Type)</span>
<span class="k">return</span> <span class="n">Py_TYPE</span><span class="p">(</span><span class="n">func</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">tp_name</span><span class="p">;</span>
</pre></div>
</div>
<p>For initialization of type objects, the current sequence</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">PyObject_HEAD_INIT</span><span class="p">(</span><span class="n">NULL</span><span class="p">)</span>
<span class="mi">0</span><span class="p">,</span> <span class="o">/*</span> <span class="n">ob_size</span> <span class="o">*/</span>
</pre></div>
</div>
<p>becomes incorrect, and must be replaced with</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">PyVarObject_HEAD_INIT</span><span class="p">(</span><span class="n">NULL</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
</pre></div>
</div>
</section>
<section id="compatibility-with-python-2-6">
<h2><a class="toc-backref" href="#compatibility-with-python-2-6" role="doc-backlink">Compatibility with Python 2.6</a></h2>
<p>To support modules that compile with both Python 2.6 and Python 3.0,
the <code class="docutils literal notranslate"><span class="pre">Py_*</span></code> macros are added to Python 2.6. The macros <code class="docutils literal notranslate"><span class="pre">Py_INCREF</span></code>
and <code class="docutils literal notranslate"><span class="pre">Py_DECREF</span></code> will be changed to cast their argument to <code class="docutils literal notranslate"><span class="pre">PyObject</span> <span class="pre">*</span></code>,
so that module authors can also explicitly declare the <code class="docutils literal notranslate"><span class="pre">ob_base</span></code>
field in modules designed for Python 2.6.</p>
</section>
<section id="copyright">
<h2><a class="toc-backref" href="#copyright" role="doc-backlink">Copyright</a></h2>
<p>This document has been placed in the public domain.</p>
</section>
</section>
<hr class="docutils" />
<p>Source: <a class="reference external" href="https://github.com/python/peps/blob/main/peps/pep-3123.rst">https://github.com/python/peps/blob/main/peps/pep-3123.rst</a></p>
<p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-3123.rst">2023-09-09 17:39:29 GMT</a></p>
</article>
<nav id="pep-sidebar">
<h2>Contents</h2>
<ul>
<li><a class="reference internal" href="#abstract">Abstract</a></li>
<li><a class="reference internal" href="#rationale">Rationale</a></li>
<li><a class="reference internal" href="#specification">Specification</a></li>
<li><a class="reference internal" href="#compatibility-with-python-2-6">Compatibility with Python 2.6</a></li>
<li><a class="reference internal" href="#copyright">Copyright</a></li>
</ul>
<br>
<a id="source" href="https://github.com/python/peps/blob/main/peps/pep-3123.rst">Page Source (GitHub)</a>
</nav>
</section>
<script src="../_static/colour_scheme.js"></script>
<script src="../_static/wrap_tables.js"></script>
<script src="../_static/sticky_banner.js"></script>
</body>
</html>