From 96e1c68ce03558589390237366866b85bd9c6229 Mon Sep 17 00:00:00 2001 From: Eisenwave Date: Wed, 19 Feb 2025 14:03:00 +0100 Subject: [PATCH] P3471R4 Standard library hardening --- source/containers.tex | 82 ++++++++++++++++++++++++++----------------- source/intro.tex | 12 ++++++- source/lib-intro.tex | 37 ++++++++++++++++--- source/macros.tex | 1 + source/numerics.tex | 2 +- source/strings.tex | 45 +++++++++++------------- source/support.tex | 24 +++++++++++++ source/utilities.tex | 36 +++++++++---------- 8 files changed, 158 insertions(+), 81 deletions(-) diff --git a/source/containers.tex b/source/containers.tex index f6ed7500fd..84445a88fa 100644 --- a/source/containers.tex +++ b/source/containers.tex @@ -1879,6 +1879,10 @@ \result \tcode{reference; const_reference} for constant \tcode{a}. +\pnum +\hardexpects +\tcode{a.empty()} is \tcode{false}. + \pnum \returns \tcode{*a.begin()} @@ -1904,6 +1908,10 @@ \result \tcode{reference; const_reference} for constant \tcode{a}. +\pnum +\hardexpects +\tcode{a.empty()} is \tcode{false}. + \pnum \effects Equivalent to: @@ -2168,7 +2176,7 @@ \keyword{void} \pnum -\expects +\hardexpects \tcode{a.empty()} is \tcode{false}. \pnum @@ -2193,7 +2201,7 @@ \keyword{void} \pnum -\expects +\hardexpects \tcode{a.empty()} is \tcode{false}. \pnum @@ -2219,6 +2227,10 @@ \result \tcode{reference; const_reference} for constant \tcode{a}. +\pnum +\hardexpects +\tcode{n < a.size()} is \tcode{true}. + \pnum \effects Equivalent to: \tcode{return *(a.begin() + n);} @@ -19233,11 +19245,13 @@ \begin{itemize} \item \range{first}{first + count} is a valid range. \item \tcode{It} models \libconcept{contiguous_iterator}. -\item -If \tcode{extent} is not equal to \tcode{dynamic_extent}, -then \tcode{count} is equal to \tcode{extent}. \end{itemize} +\pnum +\hardexpects +If \tcode{extent} is not equal to \tcode{dynamic_extent}, +then \tcode{count == extent} is \tcode{true}. + \pnum \effects Initializes \exposid{data_} with \tcode{to_address(first)} and @@ -19273,14 +19287,16 @@ \pnum \expects \begin{itemize} -\item -If \tcode{extent} is not equal to \tcode{dynamic_extent}, -then \tcode{last - first} is equal to \tcode{extent}. \item \range{first}{last} is a valid range. \item \tcode{It} models \libconcept{contiguous_iterator}. \item \tcode{End} models \tcode{\libconcept{sized_sentinel_for}}. \end{itemize} +\pnum +\hardexpects +If \tcode{extent} is not equal to \tcode{dynamic_extent}, +then \tcode{(last - first) == extent} is \tcode{true}. + \pnum \effects Initializes \exposid{data_} with \tcode{to_address(first)} and @@ -19351,14 +19367,17 @@ \pnum \expects \begin{itemize} -\item If \tcode{extent} is not equal to \tcode{dynamic_extent}, -then \tcode{ranges::size(r)} is equal to \tcode{extent}. \item \tcode{R} models \tcode{ranges::\libconcept{contiguous_range}} and \tcode{ranges::\libconcept{sized_range}}. \item If \tcode{is_const_v} is \tcode{false}, \tcode{R} models \tcode{ranges::\libconcept{borrowed_range}}. \end{itemize} +\pnum +\hardexpects +If \tcode{extent} is not equal to \tcode{dynamic_extent}, +then \tcode{ranges::size(r) == extent} is \tcode{true}. + \pnum \effects Initializes \exposid{data_} with \tcode{ranges::data(r)} and @@ -19380,9 +19399,9 @@ \tcode{is_const_v} is \tcode{true}. \pnum -\expects -If \tcode{extent} is not equal to \tcode{dynamic_extent}, then -\tcode{il.size()} is equal to \tcode{extent}. +\hardexpects +If \tcode{extent} is not equal to \tcode{dynamic_extent}, +then \tcode{il.size() == extent} is \tcode{true}. \pnum \effects @@ -19420,9 +19439,9 @@ \end{itemize} \pnum -\expects +\hardexpects If \tcode{extent} is not equal to \tcode{dynamic_extent}, -then \tcode{s.size()} is equal to \tcode{extent}. +then \tcode{s.size() == extent} is \tcode{true}. \pnum \effects @@ -19492,7 +19511,7 @@ \tcode{Count <= Extent} is \tcode{true}. \pnum -\expects +\hardexpects \tcode{Count <= size()} is \tcode{true}. \pnum @@ -19512,7 +19531,7 @@ \tcode{Count <= Extent} is \tcode{true}. \pnum -\expects +\hardexpects \tcode{Count <= size()} is \tcode{true}. \pnum @@ -19536,7 +19555,7 @@ is \tcode{true}. \pnum -\expects +\hardexpects \begin{codeblock} Offset <= size() && (Count == dynamic_extent || Count <= size() - Offset) \end{codeblock} @@ -19567,7 +19586,7 @@ \begin{itemdescr} \pnum -\expects +\hardexpects \tcode{count <= size()} is \tcode{true}. \pnum @@ -19582,7 +19601,7 @@ \begin{itemdescr} \pnum -\expects +\hardexpects \tcode{count <= size()} is \tcode{true}. \pnum @@ -19598,7 +19617,7 @@ \begin{itemdescr} \pnum -\expects +\hardexpects \begin{codeblock} offset <= size() && (count == dynamic_extent || count <= size() - offset) \end{codeblock} @@ -19656,7 +19675,7 @@ \begin{itemdescr} \pnum -\expects +\hardexpects \tcode{idx < size()} is \tcode{true}. \pnum @@ -19690,7 +19709,7 @@ \begin{itemdescr} \pnum -\expects +\hardexpects \tcode{empty()} is \tcode{false}. \pnum @@ -19709,7 +19728,7 @@ \begin{itemdescr} \pnum -\expects +\hardexpects \tcode{empty()} is \tcode{false}. \pnum @@ -23876,17 +23895,16 @@ \pnum \expects -\begin{itemize} -\item -For each rank index \tcode{r} of \tcode{extents_type}, -\tcode{static_extent(r) == dynamic_extent || static_extent(r) == other.extent(r)} -is \tcode{true}. -\item $[0, \tcode{\exposid{map_}.required_span_size()})$ is an accessible range of \exposid{ptr_} and \exposid{acc_} for values of \exposid{ptr_}, \exposid{map_}, and \exposid{acc_} after the invocation of this constructor. -\end{itemize} + +\pnum +\hardexpects +For each rank index \tcode{r} of \tcode{extents_type}, +\tcode{static_extent(r) == dynamic_extent || static_extent(r) == other.extent(r)} +is \tcode{true}. \pnum \effects @@ -23932,7 +23950,7 @@ Let \tcode{I} be \tcode{extents_type::\exposid{index-cast}(std::move(indices))}. \pnum -\expects +\hardexpects \tcode{I} is a multidimensional index in \tcode{extents()}. \begin{note} This implies that diff --git a/source/intro.tex b/source/intro.tex index 2f42ae89e3..3a41f80d5b 100644 --- a/source/intro.tex +++ b/source/intro.tex @@ -833,7 +833,8 @@ \indextext{conformance requirements!library|)} \pnum -Two kinds of implementations are defined: a \defnadj{hosted}{implementation} and a +An implementation is either a +\defnadj{hosted}{implementation} or a \defnadj{freestanding}{implementation}. A freestanding implementation is one in which execution may take place without the benefit of @@ -845,6 +846,15 @@ described in \ref{lex} through \ref{\lastcorechapter} and the subset of the library facilities described in \ref{compliance}. +\pnum +It is +\impldef{whether the implementation is a hardened implementation} +whether the implementation is a +\defnadj{hardened}{implementation}. +If it is a hardened implementation, +violating a hardened precondition +results in a contract violation\iref{structure.specifications}. + \pnum An implementation is encouraged to document its limitations in the size or complexity of the programs it can successfully process, diff --git a/source/lib-intro.tex b/source/lib-intro.tex index dad75c9aa6..5dabc1bcd7 100644 --- a/source/lib-intro.tex +++ b/source/lib-intro.tex @@ -370,8 +370,7 @@ \item \expects -the conditions -that the function assumes to hold whenever it is called; +conditions that the function assumes to hold whenever it is called; violation of any preconditions results in undefined behavior. \begin{example} An implementation can express some such conditions @@ -379,6 +378,25 @@ such as a precondition assertion\iref{dcl.contract.func}. \end{example} +\item +\hardexpects +conditions that the function assumes to hold whenever it is called. +\begin{itemize} +\item +When invoking the function in a hardened implementation, +prior to any other observable side effects of the function, +one or more contract assertions +whose predicates are as described in the hardened precondition +are evaluated with a checking semantic\iref{basic.contract.eval}. +If any of these assertions is evaluated with a non-terminating semantic +and the contract-violation handler returns, +the program has undefined behavior. +\item +When invoking the function in a non-hardened implementation, +if any hardened precondition is violated, +the program has undefined behavior. +\end{itemize} + \item \effects the actions performed by the function. @@ -434,9 +452,18 @@ If \tcode{F}'s semantics specifies any \Fundescx{Constraints} or \Fundescx{Mandates} elements, then those requirements are logically imposed prior to the \term{equivalent-to} semantics. Next, the semantics of the code sequence are determined by the -\Fundescx{Constraints}, \Fundescx{Mandates}, \Fundescx{Preconditions}, \Fundescx{Effects}, -\Fundescx{Synchronization}, \Fundescx{Postconditions}, \Fundescx{Returns}, \Fundescx{Throws}, -\Fundescx{Complexity}, \Fundescx{Remarks}, and \Fundescx{Error conditions} +\Fundescx{Constraints}, +\Fundescx{Mandates}, +\Fundescx{Preconditions}, +\Fundescx{Hardened preconditions}, +\Fundescx{Effects}, +\Fundescx{Synchronization}, +\Fundescx{Postconditions}, +\Fundescx{Returns}, +\Fundescx{Throws}, +\Fundescx{Complexity}, +\Fundescx{Remarks}, and +\Fundescx{Error conditions} specified for the function invocations contained in the code sequence. The value returned from \tcode{F} is specified by \tcode{F}'s \Fundescx{Returns} element, or if \tcode{F} has no \Fundescx{Returns} element, diff --git a/source/macros.tex b/source/macros.tex index c2b67270b5..06d5bc05e9 100644 --- a/source/macros.tex +++ b/source/macros.tex @@ -366,6 +366,7 @@ \newcommand{\constraints}{\Fundesc{Constraints}} \newcommand{\mandates}{\Fundesc{Mandates}} \newcommand{\expects}{\Fundesc{Preconditions}} +\newcommand{\hardexpects}{\Fundesc{Hardened preconditions}} \newcommand{\effects}{\Fundesc{Effects}} \newcommand{\ensures}{\Fundesc{Postconditions}} \newcommand{\returns}{\Fundesc{Returns}} diff --git a/source/numerics.tex b/source/numerics.tex index 01493894d2..fafbc9eef1 100644 --- a/source/numerics.tex +++ b/source/numerics.tex @@ -7581,7 +7581,7 @@ \begin{itemdescr} \pnum -\expects +\hardexpects \tcode{n < size()} is \tcode{true}. \pnum diff --git a/source/strings.tex b/source/strings.tex index 1fb643a61f..4625c76b4c 100644 --- a/source/strings.tex +++ b/source/strings.tex @@ -1038,8 +1038,11 @@ \begin{itemdescr} \pnum -\expects -\tcode{pos < size()}. +\hardexpects +\tcode{pos < size()} is \tcode{true}. +\begin{note} +This precondition is stronger than the one on \tcode{basic_string::operator[]}. +\end{note} \pnum \returns @@ -1048,12 +1051,6 @@ \pnum \throws Nothing. - -\pnum -\begin{note} -Unlike \tcode{basic_string::operator[]}, -\tcode{basic_string_view::operator[](size())} has undefined behavior instead of returning \tcode{charT()}. -\end{note} \end{itemdescr} \indexlibrarymember{at}{basic_string_view}% @@ -1078,8 +1075,8 @@ \begin{itemdescr} \pnum -\expects -\tcode{!empty()}. +\hardexpects +\tcode{empty()} is \tcode{false}. \pnum \returns @@ -1097,8 +1094,8 @@ \begin{itemdescr} \pnum -\expects -\tcode{!empty()}. +\hardexpects +\tcode{empty()} is \tcode{false}. \pnum \returns @@ -1136,8 +1133,8 @@ \begin{itemdescr} \pnum -\expects -\tcode{n <= size()}. +\hardexpects +\tcode{n <= size()} is \tcode{true}. \pnum \effects @@ -1151,8 +1148,8 @@ \begin{itemdescr} \pnum -\expects -\tcode{n <= size()}. +\hardexpects +\tcode{n <= size()} is \tcode{true}. \pnum \effects @@ -3093,8 +3090,8 @@ \begin{itemdescr} \pnum -\expects -\tcode{pos <= size()}. +\hardexpects +\tcode{pos <= size()} is \tcode{true}. \pnum \returns @@ -3138,8 +3135,8 @@ \begin{itemdescr} \pnum -\expects -\tcode{!empty()}. +\hardexpects +\tcode{empty()} is \tcode{false}. \pnum \effects @@ -3154,8 +3151,8 @@ \begin{itemdescr} \pnum -\expects -\tcode{!empty()}. +\hardexpects +\tcode{empty()} is \tcode{false}. \pnum \effects @@ -3939,8 +3936,8 @@ \begin{itemdescr} \pnum -\expects -\tcode{!empty()}. +\hardexpects +\tcode{empty()} is \tcode{false}. \pnum \effects diff --git a/source/support.tex b/source/support.tex index 813fc2f362..b9971694ec 100644 --- a/source/support.tex +++ b/source/support.tex @@ -848,6 +848,25 @@ #define @\defnlibxname{cpp_lib_void_t}@ 201411L // freestanding, also in \libheader{type_traits} \end{codeblock} +\pnum +Additionally, each of the following macros is defined in a hardened implementation: +\begin{codeblock} +#define @\defnlibxname{cpp_lib_hardened_array}@ 202502L // also in \libheader{array} +#define @\defnlibxname{cpp_lib_hardened_basic_string}@ 202502L // also in \libheader{string} +#define @\defnlibxname{cpp_lib_hardened_basic_string_view}@ 202502L // also in \libheader{string_view} +#define @\defnlibxname{cpp_lib_hardened_bitset}@ 202502L // also in \libheader{bitset} +#define @\defnlibxname{cpp_lib_hardened_deque}@ 202502L // also in \libheader{deque} +#define @\defnlibxname{cpp_lib_hardened_expected}@ 202502L // also in \libheader{expected} +#define @\defnlibxname{cpp_lib_hardened_forward_list}@ 202502L // also in \libheader{forward_list} +#define @\defnlibxname{cpp_lib_hardened_inplace_vector}@ 202502L // also in \libheader{inplace_vector} +#define @\defnlibxname{cpp_lib_hardened_list}@ 202502L // also in \libheader{list} +#define @\defnlibxname{cpp_lib_hardened_mdspan}@ 202502L // also in \libheader{mdspan} +#define @\defnlibxname{cpp_lib_hardened_optional}@ 202502L // also in \libheader{optional} +#define @\defnlibxname{cpp_lib_hardened_span}@ 202502L // also in \libheader{span} +#define @\defnlibxname{cpp_lib_hardened_valarray}@ 202502L // also in \libheader{valarray} +#define @\defnlibxname{cpp_lib_hardened_vector}@ 202502L // also in \libheader{vector} +\end{codeblock} + \pnum The macro \xname{cpp_lib_freestanding_operator_new} is defined to the integer literal \tcode{202306L} @@ -860,6 +879,11 @@ Freestanding implementations should only define a macro from \libheader{version} if the implementation provides the corresponding facility in its entirety. +\pnum +\recommended +A non-hardened implementation should not define macros from \libheader{version} +required for hardened implementations. + \rSec2[limits.syn]{Header \tcode{} synopsis} \indexheader{limits}% diff --git a/source/utilities.tex b/source/utilities.tex index 50eb9e67c4..f0c3f1e147 100644 --- a/source/utilities.tex +++ b/source/utilities.tex @@ -4108,8 +4108,8 @@ \begin{itemdescr} \pnum -\expects -\tcode{*this} contains a value. +\hardexpects +\tcode{has_value()} is \tcode{true}. \pnum \returns @@ -4128,8 +4128,8 @@ \begin{itemdescr} \pnum -\expects -\tcode{*this} contains a value. +\hardexpects +\tcode{has_value()} is \tcode{true}. \pnum \returns @@ -4148,8 +4148,8 @@ \begin{itemdescr} \pnum -\expects -\tcode{*this} contains a value. +\hardexpects +\tcode{has_value()} is \tcode{true}. \pnum \effects @@ -8336,7 +8336,7 @@ \begin{itemdescr} \pnum -\expects +\hardexpects \tcode{has_value()} is \tcode{true}. \pnum @@ -8352,7 +8352,7 @@ \begin{itemdescr} \pnum -\expects +\hardexpects \tcode{has_value()} is \tcode{true}. \pnum @@ -8368,7 +8368,7 @@ \begin{itemdescr} \pnum -\expects +\hardexpects \tcode{has_value()} is \tcode{true}. \pnum @@ -8439,7 +8439,7 @@ \begin{itemdescr} \pnum -\expects +\hardexpects \tcode{has_value()} is \tcode{false}. \pnum @@ -8455,7 +8455,7 @@ \begin{itemdescr} \pnum -\expects +\hardexpects \tcode{has_value()} is \tcode{false}. \pnum @@ -9407,7 +9407,7 @@ \begin{itemdescr} \pnum -\expects +\hardexpects \tcode{has_value()} is \tcode{true}. \end{itemdescr} @@ -9451,7 +9451,7 @@ \begin{itemdescr} \pnum -\expects +\hardexpects \tcode{has_value()} is \tcode{false}. \pnum @@ -9467,7 +9467,7 @@ \begin{itemdescr} \pnum -\expects +\hardexpects \tcode{has_value()} is \tcode{false}. \pnum @@ -10341,8 +10341,8 @@ \begin{itemdescr} \pnum -\expects -\tcode{pos} is valid. +\hardexpects +\tcode{pos < size()} is \tcode{true}. \pnum \returns @@ -10361,8 +10361,8 @@ \begin{itemdescr} \pnum -\expects -\tcode{pos} is valid. +\hardexpects +\tcode{pos < size()} is \tcode{true}. \pnum \returns