[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