<!DOCTYPE html>
<html>
<head>
<!-- 2024-10-16 Wed 15:49 -->
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Splicing Should Respect Access Control</title>
<meta name="author" content="Steve Downey" />
<meta name="generator" content="Org Mode" />
<style type="text/css">
  #content { max-width: 60em; margin: auto; }
  .title  { text-align: center;
             margin-bottom: .2em; }
  .subtitle { text-align: center;
              font-size: medium;
              font-weight: bold;
              margin-top:0; }
  .todo   { font-family: monospace; color: red; }
  .done   { font-family: monospace; color: green; }
  .priority { font-family: monospace; color: orange; }
  .tag    { background-color: #eee; font-family: monospace;
            padding: 2px; font-size: 80%; font-weight: normal; }
  .timestamp { color: #bebebe; }
  .timestamp-kwd { color: #5f9ea0; }
  .org-right  { margin-left: auto; margin-right: 0px;  text-align: right; }
  .org-left   { margin-left: 0px;  margin-right: auto; text-align: left; }
  .org-center { margin-left: auto; margin-right: auto; text-align: center; }
  .underline { text-decoration: underline; }
  #postamble p, #preamble p { font-size: 90%; margin: .2em; }
  p.verse { margin-left: 3%; }
  pre {
    border: 1px solid #e6e6e6;
    border-radius: 3px;
    background-color: #f2f2f2;
    padding: 8pt;
    font-family: monospace;
    overflow: auto;
    margin: 1.2em;
  }
  pre.src {
    position: relative;
    overflow: auto;
  }
  pre.src:before {
    display: none;
    position: absolute;
    top: -8px;
    right: 12px;
    padding: 3px;
    color: #555;
    background-color: #f2f2f299;
  }
  pre.src:hover:before { display: inline; margin-top: 14px;}
  /* Languages per Org manual */
  pre.src-asymptote:before { content: 'Asymptote'; }
  pre.src-awk:before { content: 'Awk'; }
  pre.src-authinfo::before { content: 'Authinfo'; }
  pre.src-C:before { content: 'C'; }
  /* pre.src-C++ doesn't work in CSS */
  pre.src-clojure:before { content: 'Clojure'; }
  pre.src-css:before { content: 'CSS'; }
  pre.src-D:before { content: 'D'; }
  pre.src-ditaa:before { content: 'ditaa'; }
  pre.src-dot:before { content: 'Graphviz'; }
  pre.src-calc:before { content: 'Emacs Calc'; }
  pre.src-emacs-lisp:before { content: 'Emacs Lisp'; }
  pre.src-fortran:before { content: 'Fortran'; }
  pre.src-gnuplot:before { content: 'gnuplot'; }
  pre.src-haskell:before { content: 'Haskell'; }
  pre.src-hledger:before { content: 'hledger'; }
  pre.src-java:before { content: 'Java'; }
  pre.src-js:before { content: 'Javascript'; }
  pre.src-latex:before { content: 'LaTeX'; }
  pre.src-ledger:before { content: 'Ledger'; }
  pre.src-lisp:before { content: 'Lisp'; }
  pre.src-lilypond:before { content: 'Lilypond'; }
  pre.src-lua:before { content: 'Lua'; }
  pre.src-matlab:before { content: 'MATLAB'; }
  pre.src-mscgen:before { content: 'Mscgen'; }
  pre.src-ocaml:before { content: 'Objective Caml'; }
  pre.src-octave:before { content: 'Octave'; }
  pre.src-org:before { content: 'Org mode'; }
  pre.src-oz:before { content: 'OZ'; }
  pre.src-plantuml:before { content: 'Plantuml'; }
  pre.src-processing:before { content: 'Processing.js'; }
  pre.src-python:before { content: 'Python'; }
  pre.src-R:before { content: 'R'; }
  pre.src-ruby:before { content: 'Ruby'; }
  pre.src-sass:before { content: 'Sass'; }
  pre.src-scheme:before { content: 'Scheme'; }
  pre.src-screen:before { content: 'Gnu Screen'; }
  pre.src-sed:before { content: 'Sed'; }
  pre.src-sh:before { content: 'shell'; }
  pre.src-sql:before { content: 'SQL'; }
  pre.src-sqlite:before { content: 'SQLite'; }
  /* additional languages in org.el's org-babel-load-languages alist */
  pre.src-forth:before { content: 'Forth'; }
  pre.src-io:before { content: 'IO'; }
  pre.src-J:before { content: 'J'; }
  pre.src-makefile:before { content: 'Makefile'; }
  pre.src-maxima:before { content: 'Maxima'; }
  pre.src-perl:before { content: 'Perl'; }
  pre.src-picolisp:before { content: 'Pico Lisp'; }
  pre.src-scala:before { content: 'Scala'; }
  pre.src-shell:before { content: 'Shell Script'; }
  pre.src-ebnf2ps:before { content: 'ebfn2ps'; }
  /* additional language identifiers per "defun org-babel-execute"
       in ob-*.el */
  pre.src-cpp:before  { content: 'C++'; }
  pre.src-abc:before  { content: 'ABC'; }
  pre.src-coq:before  { content: 'Coq'; }
  pre.src-groovy:before  { content: 'Groovy'; }
  /* additional language identifiers from org-babel-shell-names in
     ob-shell.el: ob-shell is the only babel language using a lambda to put
     the execution function name together. */
  pre.src-bash:before  { content: 'bash'; }
  pre.src-csh:before  { content: 'csh'; }
  pre.src-ash:before  { content: 'ash'; }
  pre.src-dash:before  { content: 'dash'; }
  pre.src-ksh:before  { content: 'ksh'; }
  pre.src-mksh:before  { content: 'mksh'; }
  pre.src-posh:before  { content: 'posh'; }
  /* Additional Emacs modes also supported by the LaTeX listings package */
  pre.src-ada:before { content: 'Ada'; }
  pre.src-asm:before { content: 'Assembler'; }
  pre.src-caml:before { content: 'Caml'; }
  pre.src-delphi:before { content: 'Delphi'; }
  pre.src-html:before { content: 'HTML'; }
  pre.src-idl:before { content: 'IDL'; }
  pre.src-mercury:before { content: 'Mercury'; }
  pre.src-metapost:before { content: 'MetaPost'; }
  pre.src-modula-2:before { content: 'Modula-2'; }
  pre.src-pascal:before { content: 'Pascal'; }
  pre.src-ps:before { content: 'PostScript'; }
  pre.src-prolog:before { content: 'Prolog'; }
  pre.src-simula:before { content: 'Simula'; }
  pre.src-tcl:before { content: 'tcl'; }
  pre.src-tex:before { content: 'TeX'; }
  pre.src-plain-tex:before { content: 'Plain TeX'; }
  pre.src-verilog:before { content: 'Verilog'; }
  pre.src-vhdl:before { content: 'VHDL'; }
  pre.src-xml:before { content: 'XML'; }
  pre.src-nxml:before { content: 'XML'; }
  /* add a generic configuration mode; LaTeX export needs an additional
     (add-to-list 'org-latex-listings-langs '(conf " ")) in .emacs */
  pre.src-conf:before { content: 'Configuration File'; }

  table { border-collapse:collapse; }
  caption.t-above { caption-side: top; }
  caption.t-bottom { caption-side: bottom; }
  td, th { vertical-align:top;  }
  th.org-right  { text-align: center;  }
  th.org-left   { text-align: center;   }
  th.org-center { text-align: center; }
  td.org-right  { text-align: right;  }
  td.org-left   { text-align: left;   }
  td.org-center { text-align: center; }
  dt { font-weight: bold; }
  .footpara { display: inline; }
  .footdef  { margin-bottom: 1em; }
  .figure { padding: 1em; }
  .figure p { text-align: center; }
  .equation-container {
    display: table;
    text-align: center;
    width: 100%;
  }
  .equation {
    vertical-align: middle;
  }
  .equation-label {
    display: table-cell;
    text-align: right;
    vertical-align: middle;
  }
  .inlinetask {
    padding: 10px;
    border: 2px solid gray;
    margin: 10px;
    background: #ffffcc;
  }
  #org-div-home-and-up
   { text-align: right; font-size: 70%; white-space: nowrap; }
  textarea { overflow-x: auto; }
  .linenr { font-size: smaller }
  .code-highlighted { background-color: #ffff00; }
  .org-info-js_info-navigation { border-style: none; }
  #org-info-js_console-label
    { font-size: 10px; font-weight: bold; white-space: nowrap; }
  .org-info-js_search-highlight
    { background-color: #ffff00; color: #000000; font-weight: bold; }
  .org-svg { }
</style>
<link rel="stylesheet" type="text/css" href="https://sdowney.org/css/wg21org.css"/>
<link rel="stylesheet" type="text/css" href="https://sdowney.org/css/modus-operandi-tinted.css" />
<script>
// @license magnet:?xt=urn:btih:1f739d935676111cfff4b4693e3816e664797050&amp;dn=gpl-3.0.txt GPL-v3-or-Later
     function CodeHighlightOn(elem, id)
     {
       var target = document.getElementById(id);
       if(null != target) {
         elem.classList.add("code-highlighted");
         target.classList.add("code-highlighted");
       }
     }
     function CodeHighlightOff(elem, id)
     {
       var target = document.getElementById(id);
       if(null != target) {
         elem.classList.remove("code-highlighted");
         target.classList.remove("code-highlighted");
       }
     }
// @license-end
</script>
</head>
<body>
<div id="content">
<h1 class="title">Splicing Should Respect Access Control</h1>

   <div data-fill-with="spec-metadata">
    <dl>
     <dt>Document #: <dd> P3473R0
     <dt>Date: <dd>2024-10-16
     <dt>Audience: <dd>EWG, WG21
     <dt>Reply-to: <dd><a class="p-name fn u-email email" href="mailto:sdowney@gmail.com">Steve Downey &ltsdowney@gmail.com&gt</a>
     <dt>Source: <dd><a href="https://github.com/steve-downey/wg21org"/>https://github.com/steve-downey/wg21org</a>
                 <dd>reflection-access-control.org
                 <dd>tags/P3473R0-0-gd110c7b
    </dl>
   </div>
<nav id="toc" role="doc-toc">
<h2 class="no-num no-toc no-ref" id="contents">Table of Contents</h2>

<ul class="toc">
<li><a href="#org226f902"><span class="secno">1 </span> <span class="content">P2966 Splicing does not check access by design</span></a></li>
<li><a href="#org4a0f6a6"><span class="secno">2 </span> <span class="content">Existing Examples of Access Violation</span></a></li>
<li><a href="#orgeabd8f1"><span class="secno">3 </span> <span class="content">There are use cases for writing to private members</span></a></li>
<li><a href="#orga0fd0a1"><span class="secno">4 </span> <span class="content">Read access is not Safe</span></a></li>
<li><a href="#orgc2c4aa7"><span class="secno">5 </span> <span class="content">Private names become part of API</span></a></li>
<li><a href="#orgc1561bb"><span class="secno">6 </span> <span class="content">Allowing unchecked access is a fundamental change to C++</span></a></li>
<li><a href="#org328fa29"><span class="secno">7 </span> <span class="content">References</span></a></li>
</ul>

</nav>
<div class="abstract" id="org2e28b0a">
<p>
Abstract: P2996 (<a href="#citeproc_bib_item_1">Revzin et al. 2024</a>) ignores access control for member access splice. Member access control should always be respected.
</p>

</div>
<div id="outline-container-org226f902" class="outline-2">
<h2 class="heading" id="org226f902" ><span class="section-number-2">1.</span> P2966 Splicing does not check access by design</h2>
<div class="outline-text-2" id="text-1">
<div class="org-src-container">
<pre class="src src-C++"><span class="org-preprocessor">#include</span> <span class="org-string">&lt;experimental/meta&gt;</span>
<span class="org-preprocessor">#include</span> <span class="org-string">&lt;iostream&gt;</span>
<span class="org-preprocessor">#include</span> <span class="org-string">&lt;utility&gt;</span>

<span class="org-keyword">class</span> <span class="org-type">S</span> {
    <span class="org-type">int</span> <span class="org-variable-name">priv</span>;
<span class="org-keyword">public</span>:
    <span class="org-function-name">S</span>() : priv(0) {}
};

<span class="org-keyword">consteval</span> <span class="org-keyword">auto</span> <span class="org-function-name">member_named</span>(<span class="org-constant">std</span>::<span class="org-type">string_view</span> <span class="org-variable-name">name</span>) {
  <span class="org-keyword">for</span> (<span class="org-constant">std</span>::<span class="org-constant">meta</span>::info <span class="org-type">field</span> : nonstatic_data_members_of(^S)) {
    <span class="org-keyword">if</span> (has_identifier(field) &amp;&amp; identifier_of(field) == name)
      <span class="org-keyword">return</span> field;
  }
  <span class="org-constant">std</span>::unreachable();
}

<span class="org-type">int</span> <span class="org-function-name">main</span>() {
  <span class="org-type">S</span> <span class="org-variable-name">s</span>;
  s.[:member_named(<span class="org-string">"priv"</span>):] = 42;
  <span class="org-keyword">return</span> s.[:member_named(<span class="org-string">"priv"</span>):];
}
</pre>
</div>
<p>
<a href="https://godbolt.org/z/6b1hf8631">https://godbolt.org/z/6b1hf8631</a>
</p>

<p>
This example is just slightly modified from an example in 3.3 of P2966. <a href="https://godbolt.org/z/MEPb78ece">https://godbolt.org/z/MEPb78ece</a>
</p>

<blockquote>
<p>
Note that a “member access splice” like <code>s.[:member_number(1):]</code> is a more direct member access mechanism than the traditional syntax. It doesn’t involve member name lookup, access checking, or — if the spliced reflection value represents a member function — overload resolution.
</p>
</blockquote>
</div>
</div>
<div id="outline-container-org4a0f6a6" class="outline-2">
<h2 class="heading" id="org4a0f6a6" ><span class="section-number-2">2.</span> Existing Examples of Access Violation</h2>
<div class="outline-text-2" id="text-2">
<p>
It has been argued that C++ has existing mechanisms for access control avoidance. One in particular was presented to me.
</p>

<div class="org-src-container">
<pre class="src src-C++"><span class="org-keyword">class</span> <span class="org-type">B</span> {
    <span class="org-type">int</span> <span class="org-variable-name">priv</span>;
  <span class="org-keyword">public</span>:
    <span class="org-type">int</span> <span class="org-function-name">get</span>() {<span class="org-keyword">return</span> priv;}
 };

<span class="org-type">int</span> <span class="org-constant">B</span>::<span class="org-function-name">* get_private</span>();

<span class="org-keyword">template</span> &lt;<span class="org-type">int</span> <span class="org-constant">B</span>::<span class="org-variable-name">* M</span>&gt;
<span class="org-keyword">struct</span> <span class="org-type">Robber</span> {
   <span class="org-keyword">friend</span> <span class="org-type">int</span> <span class="org-constant">B</span>::<span class="org-function-name">* get_private</span>() { <span class="org-keyword">return</span> M; };
};

<span class="org-keyword">template</span> <span class="org-keyword">struct</span> <span class="org-type">Robber</span>&lt;&amp;<span class="org-constant">B</span>::priv&gt;;

<span class="org-type">int</span> <span class="org-function-name">main</span>() {
    <span class="org-type">B</span> <span class="org-variable-name">b</span>;
    b.*get_private() = 42;
    <span class="org-keyword">return</span> b.get();
}
</pre>
</div>
<p>
<a href="https://godbolt.org/z/eYvb97G91">https://godbolt.org/z/eYvb97G91</a>
</p>


<p>
Simplifying and making the mechanism less general, but possibly somewhat clearer:
</p>

<div class="org-src-container">
<pre class="src src-C++"><span class="org-keyword">class</span> <span class="org-type">B</span> { <span class="org-type">int</span> <span class="org-variable-name">priv</span>;};

<span class="org-keyword">using</span> <span class="org-type">tag</span> = <span class="org-type">int</span> <span class="org-constant">B</span>::*;
<span class="org-type">tag</span> <span class="org-function-name">get_private</span>();

<span class="org-keyword">template</span> &lt;<span class="org-type">tag</span> <span class="org-variable-name">M</span>&gt;
<span class="org-keyword">struct</span> <span class="org-type">Robber</span> {
   <span class="org-keyword">friend</span> <span class="org-type">tag</span> <span class="org-function-name">get_private</span>() { <span class="org-keyword">return</span> M; };
};

<span class="org-keyword">template</span> <span class="org-keyword">struct</span> <span class="org-type">Robber</span>&lt;&amp;<span class="org-constant">B</span>::priv&gt;;

<span class="org-type">int</span> <span class="org-function-name">main</span>() {
    <span class="org-type">B</span> <span class="org-variable-name">b</span>;
    b.*get_private() = 42;
    <span class="org-keyword">return</span> b.*get_private();
}
</pre>
</div>
<p>
<a href="https://godbolt.org/z/1jqGMs1cr">https://godbolt.org/z/1jqGMs1cr</a>
</p>

<p>
Access to the name <code class="src src-C++"><span class="org-constant">B</span>::priv</code> to take its address is not checked when creating the explicit instantiation of <code class="src src-C++">Robber</code>. This includes the instantiation of the friend function defined in the body of the template, <code>get_private</code>, which has access to the template parameter object <code>M</code>, which holds the value of the member pointer to <code class="src src-C++"><span class="org-constant">B</span>::priv</code>. Access control would normally apply to naming the type <code class="src src-C++"><span class="org-type">Robber</span>&lt;Getter, &amp;<span class="org-constant">B</span>::priv&gt;</code>, but the friend function can be named without reference to the type that provided the definition. Access control is thereby skirted. The <code>Getter</code> type acts as a selection mechanism for overloads of <code>get_private</code>, so that no access check is violated at the call site. The <code>Robber</code> type would not even need to be visible in the same TU.
</p>

<p>
I believe this is a defect, even if there is no clear point at which to fix the problem. I do not believe it was a design choice to allow general avoidance of access control.
</p>
</div>
</div>
<div id="outline-container-orgeabd8f1" class="outline-2">
<h2 class="heading" id="orgeabd8f1" ><span class="section-number-2">3.</span> There are use cases for writing to private members</h2>
<div class="outline-text-2" id="text-3">
<p>
I believe these are not use cases that C++ should support. Deserialization is one that comes to mind, where writing to private data is imperative. Serialization/Deserialization is an important use case for reflection.  It should be limited to code instantiated in contexts that have access to the members otherwise, without reflection, through member functions of a type, or friends that the type declares. While writing a deserializer for a type that doesn't support it now would be wonderful, the mechanism of writing to elements without checking access control is too blunt a tool, and too prone to casual misuse.
</p>

<p>
There is no way to restrict a general facility for only good and approved purposes. Language mechanisms are neutral. The tools will be misused and useful mechanisms will have to be banned because of the potential for misuse. The programmer cannot be trusted.
</p>
</div>
</div>
<div id="outline-container-orga0fd0a1" class="outline-2">
<h2 class="heading" id="orga0fd0a1" ><span class="section-number-2">4.</span> Read access is not Safe</h2>
<div class="outline-text-2" id="text-4">
<p>
Even read access is not generally safe in a multithreaded environment, which is becoming more and more common. Reads from containers are unsafe, and usually undefined, in the face of any write operation. Reads from non-atomic data is also unsafe if the data is being written. Other languages mitigate this in various ways. Rust's type system makes read and write access safe. Java has monitors for classes and objects idiomatically used to control multithreaded access. C++ has no such conventions. This sort of bug is common today in ostream operators, of course, and in formatters. Making a formatter safe requires understanding of the type, and from the outside it will not be clear to a library what the required techniques are.
</p>
</div>
</div>
<div id="outline-container-orgc2c4aa7" class="outline-2">
<h2 class="heading" id="orgc2c4aa7" ><span class="section-number-2">5.</span> Private names become part of API</h2>
<div class="outline-text-2" id="text-5">
<p>
The names of private data become part of the API of an object as changing them becomes a break in client code. Even without an ABI implication. Even if the client code can be changed, it increases the cost of any refactoring. Hyrum's Law gets a new tool to couple dependencies. That it is out of contract behavior, and that the implementer is entitled to break the code of the client, is of little use in practice.
</p>

<p>
This is, admittedly, an exercise in line drawing. Access control is checked last, after name lookup, so private names can affect compilation today. However, someone checking for the existence of the name <code>lock</code> without using it in any way is not the same level of risk as someone looking for it and locking it from the outside.
</p>
</div>
</div>
<div id="outline-container-orgc1561bb" class="outline-2">
<h2 class="heading" id="orgc1561bb" ><span class="section-number-2">6.</span> Allowing unchecked access is a fundamental change to C++</h2>
<div class="outline-text-2" id="text-6">
<p>
We have so far resisted the temptation to deliberately provide tools to ignore access control.
</p>

<p>
We should continue to do so.
</p>
</div>
</div>
<div id="outline-container-org328fa29" class="outline-2">
<h2 class="heading" id="org328fa29" ><span class="section-number-2">7.</span> References</h2>
<div class="outline-text-2" id="text-7">
<style>.csl-entry{text-indent: -1.5em; margin-left: 1.5em;}</style><div class="csl-bib-body">
  <div class="csl-entry"><a id="citeproc_bib_item_1"></a>Revzin, Barry, Wyatt Childers, Peter Dimov, Andrew Sutton, Faisal Vali, Daveed Vandevoorde, and Dan Katz. 2024. “P2996R5: Reflection for c++26.” https://wg21.link/p2996r5; WG21.</div>
</div>

<p>
Exported: 2024-10-16 15:49:35
</p>
</div>
</div>
</div>
</body>
</html>
