[latex3-commits] [git/latex3] master: Check that constants are never subject to local/global assignments (f59b697)

Bruno Le Floch bruno at le-floch.fr
Wed Nov 29 08:16:40 CET 2017


Repository : https://github.com/latex3/latex3
On branch  : master
Link       : https://github.com/latex3/latex3/commit/f59b697848005ddb46edda8de1e696d5054b946d

>---------------------------------------------------------------

commit f59b697848005ddb46edda8de1e696d5054b946d
Author: Bruno Le Floch <bruno at le-floch.fr>
Date:   Wed Nov 29 02:16:40 2017 -0500

    Check that constants are never subject to local/global assignments
    
    Of course using low-level \cs_ functions one can still assign to constants.


>---------------------------------------------------------------

f59b697848005ddb46edda8de1e696d5054b946d
 l3kernel/l3basics.dtx     |   85 +++++++++++++++++++++++++++------------------
 l3kernel/l3candidates.dtx |    1 +
 l3kernel/l3int.dtx        |    4 ++-
 l3kernel/l3quark.dtx      |    7 +++-
 l3kernel/l3skip.dtx       |    3 ++
 l3kernel/l3tl.dtx         |    2 ++
 6 files changed, 66 insertions(+), 36 deletions(-)

diff --git a/l3kernel/l3basics.dtx b/l3kernel/l3basics.dtx
index 0b56839..c38a89d 100644
--- a/l3kernel/l3basics.dtx
+++ b/l3kernel/l3basics.dtx
@@ -1259,21 +1259,31 @@
 %   \cs{cs_if_exist_p:N}, and if not raises a kernel-level error.
 % \end{function}
 %
+% \begin{function}{\__debug_chk_var_scope:NN}
+%   \begin{syntax}
+%     \cs{__debug_chk_var_scope:NN} \meta{scope} \meta{var}
+%   \end{syntax}
+%   Checks the \meta{var} has the correct \meta{scope}, and if not
+%   raises a kernel-level error.  This function is only created if
+%   debugging is enabled.  The \meta{scope} is a single letter |l|, |g|,
+%   |c| denoting local variables, global variables, or constants.  More
+%   precisely, if the variable name starts with a letter and an
+%   underscore (normal \pkg{expl3} convention) the function checks that
+%   this single letter matches the \meta{scope}.  Otherwise the function
+%   cannot know the scope \meta{var} the first time: instead, it defines
+%   |\__debug_chk_/|\meta{var name} to store that information for the
+%   next call.  Thus, if a given \meta{var} is subject to assignments of
+%   different scopes a kernel error will result.
+% \end{function}
+%
 % \begin{function}{\__debug_chk_var_local:N, \__debug_chk_var_global:N}
 %   \begin{syntax}
 %     \cs{__debug_chk_var_local:N} \meta{var}
 %     \cs{__debug_chk_var_global:N} \meta{var}
 %   \end{syntax}
-%   These functions are only created if debugging is enabled.  They
-%   check that \meta{var} is a local/global variable, and if not raises
-%   a kernel-level error.  More precisely, if the variable name starts
-%   with a letter and an underscore (normal \pkg{expl3} convention) the
-%   functions check that this single letter is |l| or |g| as
-%   appropriate.  Otherwise the functions cannot know the first time
-%   whether \meta{var} is local or global: instead, they define
-%   |\__debug_chk_/|\meta{var name} to store the information, and if
-%   both \cs{__debug_chk_var_local:N} and \cs{__debug_chk_var_global:N}
-%   are called on the same variable a kernel-level error is raised.
+%   Applies \cs{__debug_chk_var_exist:N} \meta{var}, then
+%   \cs{__debug_chk_var_scope:NN} \meta{scope} \meta{var}, where
+%   \meta{scope} is |l| or~|g|.
 % \end{function}
 %
 % \begin{function}{\__debug_log:x}
@@ -1858,14 +1868,16 @@
 % \begin{macro}[int]{\@@_chk_var_exist:N}
 % \begin{macro}[int]{\@@_chk_cs_exist:N, \@@_chk_cs_exist:c}
 % \begin{macro}[int]{\@@_chk_var_local:N, \@@_chk_var_global:N}
+% \begin{macro}[int]{\@@_chk_var_scope:NN}
 %   When debugging is enabled these two functions set up functions that
 %   test their argument (when \texttt{check-declarations} is active)
 %   \begin{itemize}
 %     \item \cs{@@_chk_var_exist:N} and \cs{@@_chk_cs_exist:N}, two
 %       functions that test that their argument is defined;
-%     \item \cs{@@_chk_var_local:N} and \cs{@@_chk_var_global:N}, two
-%       functions that check that their argument is a local (resp.\@
-%       global) variable.
+%     \item \cs{@@_chk_var_scope:NN} that checks that its argument |#2|
+%       has scope |#1|.
+%     \item \cs{@@_chk_var_local:N} and \cs{@@_chk_var_global:N} that
+%       perform both checks.
 %   \end{itemize}
 %    \begin{macrocode}
 \@@:TF
@@ -1890,27 +1902,31 @@
                   { \token_to_str:N ##1 }
               }
           }
+        \cs_set_protected:Npn \@@_chk_var_scope:NN
+          {
+            \@@_suspended:T \use_none:nnn
+            \@@_chk_var_scope_aux:NN
+          }
         \cs_set_protected:Npn \@@_chk_var_local:N ##1
           {
             \@@_suspended:T \use_none:nnnnn
             \@@_chk_var_exist:N ##1
-            \@@_chk_var_local_aux:NN l ##1
+            \@@_chk_var_scope_aux:NN l ##1
           }
         \cs_set_protected:Npn \@@_chk_var_global:N ##1
           {
             \@@_suspended:T \use_none:nnnnn
             \@@_chk_var_exist:N ##1
-            \@@_chk_var_local_aux:NN g ##1
+            \@@_chk_var_scope_aux:NN g ##1
           }
       }
     \exp_args:Nc \cs_set_protected:Npn { @@_check-declarations_off: }
       {
         \cs_set_protected:Npn \@@_chk_var_exist:N ##1 { }
         \cs_set_protected:Npn \@@_chk_cs_exist:N ##1 { }
-        \cs_set_protected:Npn \@@_chk_var_local:N ##1
-          { \@@_chk_var_exist:N ##1 }
-        \cs_set_protected:Npn \@@_chk_var_global:N ##1
-          { \@@_chk_var_exist:N ##1 }
+        \cs_set_protected:Npn \@@_chk_var_local:N ##1 { }
+        \cs_set_protected:Npn \@@_chk_var_global:N ##1 { }
+        \cs_set_protected:Npn \@@_chk_var_scope:NN ##1##2 { }
       }
     \cs_set_protected:Npn \@@_chk_cs_exist:c
       { \exp_args:Nc \@@_chk_cs_exist:N }
@@ -1926,40 +1942,41 @@
 % \end{macro}
 % \end{macro}
 % \end{macro}
+% \end{macro}
 %
-% \begin{macro}[aux]{\@@_chk_var_local_aux:NN}
-% \begin{macro}[aux]{\@@_chk_var_local_aux:Nn}
-% \begin{macro}[aux]{\@@_chk_var_local_aux:NNn}
+% \begin{macro}[aux]{\@@_chk_var_scope_aux:NN}
+% \begin{macro}[aux]{\@@_chk_var_scope_aux:Nn}
+% \begin{macro}[aux]{\@@_chk_var_scope_aux:NNn}
 %   First check whether the name of the variable |#2| starts with
-%   \meta{letter}|_|.  If it does then pass that letter, the target
-%   letter |l| or |g|, and the variable name to
-%   \cs{@@_chk_var_local_aux:NNn}.  That function compares the two
+%   \meta{letter}|_|.  If it does then pass that letter, the
+%   \meta{scope}, and the variable name to
+%   \cs{@@_chk_var_scope_aux:NNn}.  That function compares the two
 %   letters and triggers an error if they differ (the \cs{scan_stop:}
 %   case is not reachable here).  If the second character was not |_|
 %   then pass the same data to the same auxiliary, except for its first
 %   argument which is now a control sequence.  That control sequence is
 %   actually a token list (but to avoid triggering the checking code we
 %   manipulate it using \cs{cs_set_nopar:Npn}) containing a single
-%   letter |l| or |g| according to what the first assignment to the
+%   letter \meta{scope} according to what the first assignment to the
 %   given variable was.
 %    \begin{macrocode}
 \@@:TF
   {
-    \cs_set_protected:Npn \@@_chk_var_local_aux:NN #1#2
-      { \exp_args:NNf \@@_chk_var_local_aux:Nn #1 { \cs_to_str:N #2 } }
-    \cs_set_protected:Npn \@@_chk_var_local_aux:Nn #1#2
+    \cs_set_protected:Npn \@@_chk_var_scope_aux:NN #1#2
+      { \exp_args:NNf \@@_chk_var_scope_aux:Nn #1 { \cs_to_str:N #2 } }
+    \cs_set_protected:Npn \@@_chk_var_scope_aux:Nn #1#2
       {
-        \if:w _ \use_i:nn \tl_head:w #2 ? ? \q_stop
-          \exp_after:wN \@@_chk_var_local_aux:NNn
-            \tl_head:w #2 ? \q_stop
+        \if:w _ \use_i:nn \use_i_delimit_by_q_stop:nw #2 ? ? \q_stop
+          \exp_after:wN \@@_chk_var_scope_aux:NNn
+            \use_i_delimit_by_q_stop:nw #2 ? \q_stop
             #1 {#2}
         \else:
-          \exp_args:Nc \@@_chk_var_local_aux:NNn
+          \exp_args:Nc \@@_chk_var_scope_aux:NNn
             { @@_chk_/ #2 }
             #1 {#2}
         \fi:
       }
-    \cs_set_protected:Npn \@@_chk_var_local_aux:NNn #1#2#3
+    \cs_set_protected:Npn \@@_chk_var_scope_aux:NNn #1#2#3
       {
         \if:w #1 #2
         \else:
diff --git a/l3kernel/l3candidates.dtx b/l3kernel/l3candidates.dtx
index 6de5708..fb2bbfb 100644
--- a/l3kernel/l3candidates.dtx
+++ b/l3kernel/l3candidates.dtx
@@ -1977,6 +1977,7 @@
 % \begin{macro}[added = 2017-11-28]{\bool_const:Nn, \bool_const:cn}
 %   A merger between \cs{tl_const:Nn} and \cs{bool_set:Nn}.
 %    \begin{macrocode}
+\__debug_patch:nnNNpn { \__debug_chk_var_scope:NN c #1 } { }
 \cs_new_protected:Npn \bool_const:Nn #1#2
   {
     \__chk_if_free_cs:N #1
diff --git a/l3kernel/l3int.dtx b/l3kernel/l3int.dtx
index a793ebb..755c673 100644
--- a/l3kernel/l3int.dtx
+++ b/l3kernel/l3int.dtx
@@ -1203,7 +1203,9 @@
 %   We cannot use \cs{int_gset:Nn} because (when |check-declarations| is
 %   enabled) this runs some checks that constants would fail.
 %    \begin{macrocode}
-\__debug_patch_args:nNNpn
+\__debug_patch_args:nnnNNpn
+  { \__debug_chk_var_scope:NN c #1 }
+  { }
   { {#1} { \__debug_chk_expr:nNnN {#2} \@@_eval:w { } \int_const:Nn } }
 \cs_new_protected:Npn \int_const:Nn #1#2
   {
diff --git a/l3kernel/l3quark.dtx b/l3kernel/l3quark.dtx
index a62bd7e..0eb8f86 100644
--- a/l3kernel/l3quark.dtx
+++ b/l3kernel/l3quark.dtx
@@ -375,7 +375,12 @@
 % \UnitTested
 %    Allocate a new quark.
 %    \begin{macrocode}
-\cs_new_protected:Npn \quark_new:N #1 { \tl_const:Nn #1 {#1} }
+\__debug_patch:nnNNpn { \__debug_chk_var_scope:NN q #1 } { }
+\cs_new_protected:Npn \quark_new:N #1
+  {
+    \__chk_if_free_cs:N #1
+    \cs_gset_nopar:Npn #1 {#1}
+  }
 %    \end{macrocode}
 % \end{macro}
 %
diff --git a/l3kernel/l3skip.dtx b/l3kernel/l3skip.dtx
index 348ac29..15b15d6 100644
--- a/l3kernel/l3skip.dtx
+++ b/l3kernel/l3skip.dtx
@@ -1064,6 +1064,7 @@
 %   \cs{dim_eval:n} to avoid needing a debugging patch that wraps the
 %   expression in checking code.
 %    \begin{macrocode}
+\__debug_patch:nnNNpn { \__debug_chk_var_scope:NN c #1 } { }
 \cs_new_protected:Npn \dim_const:Nn #1#2
   {
     \dim_new:N #1
@@ -1658,6 +1659,7 @@
 %   even for constants.  See \cs{dim_const:Nn} for why we cannot use
 %   \cs{skip_gset:Nn}.
 %    \begin{macrocode}
+\__debug_patch:nnNNpn { \__debug_chk_var_scope:NN c #1 } { }
 \cs_new_protected:Npn \skip_const:Nn #1#2
   {
     \skip_new:N #1
@@ -1949,6 +1951,7 @@
 % \begin{macro}{\muskip_const:Nn, \muskip_const:cn}
 %   See \cs{skip_const:Nn}.
 %    \begin{macrocode}
+\__debug_patch:nnNNpn { \__debug_chk_var_scope:NN c #1 } { }
 \cs_new_protected:Npn \muskip_const:Nn #1#2
   {
     \muskip_new:N #1
diff --git a/l3kernel/l3tl.dtx b/l3kernel/l3tl.dtx
index 3e18f8d..c8e4f06 100644
--- a/l3kernel/l3tl.dtx
+++ b/l3kernel/l3tl.dtx
@@ -1131,11 +1131,13 @@
 % \begin{macro}{\tl_const:Nn, \tl_const:Nx, \tl_const:cn, \tl_const:cx}
 %   Constants are also easy to generate.
 %    \begin{macrocode}
+\__debug_patch:nnNNpn { \__debug_chk_var_scope:NN c #1 } { }
 \cs_new_protected:Npn \tl_const:Nn #1#2
   {
     \__chk_if_free_cs:N #1
     \cs_gset_nopar:Npx #1 { \exp_not:n {#2} }
   }
+\__debug_patch:nnNNpn { \__debug_chk_var_scope:NN c #1 } { }
 \cs_new_protected:Npn \tl_const:Nx #1#2
   {
     \__chk_if_free_cs:N #1





More information about the latex3-commits mailing list