the documentation of the variable at the end of the chain of aliases.
This function returns @var{base-variable}.
+
+If the resulting variable definition chain would be circular, then
+Emacs will signal a @code{cyclic-variable-indirection} error.
@end defun
Variable aliases are convenient for replacing an old name for a
This function returns the variable at the end of the chain of aliases
of @var{variable}. If @var{variable} is not a symbol, or if @var{variable} is
not defined as an alias, the function returns @var{variable}.
-
-This function signals a @code{cyclic-variable-indirection} error if
-there is a loop in the chain of symbols.
@end defun
@example
connection has no associated user).
+++
-** 'fset' and 'defalias' now signal an error for circular alias chains.
-Previously, 'fset' and 'defalias' could be made to build circular
-function indirection chains as in
+** 'fset', 'defalias' and 'defvaralias' now signal an error for cyclic aliases.
+Previously, 'fset', 'defalias' and 'defvaralias' could be made to
+build circular function and variable indirection chains as in
(defalias 'able 'baker)
(defalias 'baker 'able)
-but trying to call them would often make Emacs hang. Now, an attempt
+but trying to use them would sometimes make Emacs hang. Now, an attempt
to create such a loop results in an error.
-Since circular alias chains now cannot occur, 'function-alias-p' and
-'indirect-function' will never signal an error. Their second
-'noerror' arguments have no effect and are therefore obsolete.
+Since circular alias chains now cannot occur, 'function-alias-p',
+'indirect-function' and 'indirect-variable' will never signal an error.
+Their 'noerror' arguments have no effect and are therefore obsolete.
\f
* Changes in Emacs 30.1 on Non-Free Operating Systems
start:
switch (sym->u.s.redirect)
{
- case SYMBOL_VARALIAS: sym = indirect_variable (sym); goto start;
+ case SYMBOL_VARALIAS: sym = SYMBOL_ALIAS (sym); goto start;
case SYMBOL_PLAINVAL: result = SYMBOL_VAL (sym); break;
case SYMBOL_LOCALIZED:
{ /* Look in local_var_alist. */
switch (sym->u.s.redirect)
{
case SYMBOL_PLAINVAL: valcontents = SYMBOL_VAL (sym); break;
- case SYMBOL_VARALIAS: sym = indirect_variable (sym); goto start;
+ case SYMBOL_VARALIAS: sym = SYMBOL_ALIAS (sym); goto start;
case SYMBOL_LOCALIZED:
{
struct Lisp_Buffer_Local_Value *blv = SYMBOL_BLV (sym);
Getting and Setting Values of Symbols
***********************************************************************/
-/* Return the symbol holding SYMBOL's value. Signal
- `cyclic-variable-indirection' if SYMBOL's chain of variable
- indirections contains a loop. */
-
-struct Lisp_Symbol *
-indirect_variable (struct Lisp_Symbol *symbol)
-{
- struct Lisp_Symbol *tortoise, *hare;
-
- hare = tortoise = symbol;
-
- while (hare->u.s.redirect == SYMBOL_VARALIAS)
- {
- hare = SYMBOL_ALIAS (hare);
- if (hare->u.s.redirect != SYMBOL_VARALIAS)
- break;
-
- hare = SYMBOL_ALIAS (hare);
- tortoise = SYMBOL_ALIAS (tortoise);
-
- if (hare == tortoise)
- {
- Lisp_Object tem;
- XSETSYMBOL (tem, symbol);
- xsignal1 (Qcyclic_variable_indirection, tem);
- }
- }
-
- return hare;
-}
-
-
DEFUN ("indirect-variable", Findirect_variable, Sindirect_variable, 1, 1, 0,
doc: /* Return the variable at the end of OBJECT's variable chain.
If OBJECT is a symbol, follow its variable indirections (if any), and
return the variable at the end of the chain of aliases. See Info node
`(elisp)Variable Aliases'.
-If OBJECT is not a symbol, just return it. If there is a loop in the
-chain of aliases, signal a `cyclic-variable-indirection' error. */)
+If OBJECT is not a symbol, just return it. */)
(Lisp_Object object)
{
if (SYMBOLP (object))
{
- struct Lisp_Symbol *sym = indirect_variable (XSYMBOL (object));
+ struct Lisp_Symbol *sym = XSYMBOL (object);
+ while (sym->u.s.redirect == SYMBOL_VARALIAS)
+ sym = SYMBOL_ALIAS (sym);
XSETSYMBOL (object, sym);
}
return object;
start:
switch (sym->u.s.redirect)
{
- case SYMBOL_VARALIAS: sym = indirect_variable (sym); goto start;
+ case SYMBOL_VARALIAS: sym = SYMBOL_ALIAS (sym); goto start;
case SYMBOL_PLAINVAL: return SYMBOL_VAL (sym);
case SYMBOL_LOCALIZED:
{
start:
switch (sym->u.s.redirect)
{
- case SYMBOL_VARALIAS: sym = indirect_variable (sym); goto start;
+ case SYMBOL_VARALIAS: sym = SYMBOL_ALIAS (sym); goto start;
case SYMBOL_PLAINVAL: SET_SYMBOL_VAL (sym , newval); return;
case SYMBOL_LOCALIZED:
{
start:
switch (sym->u.s.redirect)
{
- case SYMBOL_VARALIAS: sym = indirect_variable (sym); goto start;
+ case SYMBOL_VARALIAS: sym = SYMBOL_ALIAS (sym); goto start;
case SYMBOL_PLAINVAL: return SYMBOL_VAL (sym);
case SYMBOL_LOCALIZED:
{
start:
switch (sym->u.s.redirect)
{
- case SYMBOL_VARALIAS: sym = indirect_variable (sym); goto start;
+ case SYMBOL_VARALIAS: sym = SYMBOL_ALIAS (sym); goto start;
case SYMBOL_PLAINVAL: set_internal (symbol, value, Qnil, bindflag); return;
case SYMBOL_LOCALIZED:
{
start:
switch (sym->u.s.redirect)
{
- case SYMBOL_VARALIAS: sym = indirect_variable (sym); goto start;
+ case SYMBOL_VARALIAS: sym = SYMBOL_ALIAS (sym); goto start;
case SYMBOL_PLAINVAL:
forwarded = 0; valcontents.value = SYMBOL_VAL (sym);
if (BASE_EQ (valcontents.value, Qunbound))
start:
switch (sym->u.s.redirect)
{
- case SYMBOL_VARALIAS: sym = indirect_variable (sym); goto start;
+ case SYMBOL_VARALIAS: sym = SYMBOL_ALIAS (sym); goto start;
case SYMBOL_PLAINVAL:
forwarded = 0; valcontents.value = SYMBOL_VAL (sym); break;
case SYMBOL_LOCALIZED:
start:
switch (sym->u.s.redirect)
{
- case SYMBOL_VARALIAS: sym = indirect_variable (sym); goto start;
+ case SYMBOL_VARALIAS: sym = SYMBOL_ALIAS (sym); goto start;
case SYMBOL_PLAINVAL: return variable;
case SYMBOL_FORWARDED:
{
start:
switch (sym->u.s.redirect)
{
- case SYMBOL_VARALIAS: sym = indirect_variable (sym); goto start;
+ case SYMBOL_VARALIAS: sym = SYMBOL_ALIAS (sym); goto start;
case SYMBOL_PLAINVAL: return Qnil;
case SYMBOL_LOCALIZED:
{
start:
switch (sym->u.s.redirect)
{
- case SYMBOL_VARALIAS: sym = indirect_variable (sym); goto start;
+ case SYMBOL_VARALIAS: sym = SYMBOL_ALIAS (sym); goto start;
case SYMBOL_PLAINVAL: return Qnil;
case SYMBOL_LOCALIZED:
{
start:
switch (sym->u.s.redirect)
{
- case SYMBOL_VARALIAS: sym = indirect_variable (sym); goto start;
+ case SYMBOL_VARALIAS: sym = SYMBOL_ALIAS (sym); goto start;
case SYMBOL_PLAINVAL: return Qnil;
case SYMBOL_FORWARDED:
{
or of the variable at the end of the chain of aliases, if BASE-VARIABLE is
itself an alias. If NEW-ALIAS is bound, and BASE-VARIABLE is not,
then the value of BASE-VARIABLE is set to that of NEW-ALIAS.
-The return value is BASE-VARIABLE. */)
+The return value is BASE-VARIABLE.
+
+If the resulting chain of variable definitions would contain a loop,
+signal a `cyclic-variable-indirection' error. */)
(Lisp_Object new_alias, Lisp_Object base_variable, Lisp_Object docstring)
{
- struct Lisp_Symbol *sym;
-
CHECK_SYMBOL (new_alias);
CHECK_SYMBOL (base_variable);
error ("Cannot make a constant an alias: %s",
SDATA (SYMBOL_NAME (new_alias)));
- sym = XSYMBOL (new_alias);
+ struct Lisp_Symbol *sym = XSYMBOL (new_alias);
+
+ /* Ensure non-circularity. */
+ struct Lisp_Symbol *s = XSYMBOL (base_variable);
+ for (;;)
+ {
+ if (s == sym)
+ xsignal1 (Qcyclic_variable_indirection, base_variable);
+ if (s->u.s.redirect != SYMBOL_VARALIAS)
+ break;
+ s = SYMBOL_ALIAS (s);
+ }
switch (sym->u.s.redirect)
{
switch (sym->u.s.redirect)
{
case SYMBOL_VARALIAS:
- sym = indirect_variable (sym); XSETSYMBOL (symbol, sym); goto start;
+ sym = SYMBOL_ALIAS (sym); XSETSYMBOL (symbol, sym); goto start;
case SYMBOL_PLAINVAL:
/* The most common case is that of a non-constant symbol with a
trivial value. Make that as fast as we can. */
extern intmax_t cons_to_signed (Lisp_Object, intmax_t, intmax_t);
extern uintmax_t cons_to_unsigned (Lisp_Object, uintmax_t);
-extern struct Lisp_Symbol *indirect_variable (struct Lisp_Symbol *);
extern AVOID args_out_of_range (Lisp_Object, Lisp_Object);
extern AVOID circular_list (Lisp_Object);
extern Lisp_Object do_symval_forwarding (lispfwd);
)
(should (eq eval-test--local-var 'global)))
+(ert-deftest eval-tests-defvaralias ()
+ (defvar eval-tests--my-var 'coo)
+ (defvaralias 'eval-tests--my-var1 'eval-tests--my-var)
+ (defvar eval-tests--my-var1)
+ (should (equal eval-tests--my-var 'coo))
+ (should (equal eval-tests--my-var1 'coo))
+
+ (defvaralias 'eval-tests--my-a 'eval-tests--my-b)
+ (defvaralias 'eval-tests--my-b 'eval-tests--my-c)
+
+ (should-error (defvaralias 'eval-tests--my-c 'eval-tests--my-c)
+ :type 'cyclic-variable-indirection)
+ (defvaralias 'eval-tests--my-d 'eval-tests--my-a)
+ (should-error (defvaralias 'eval-tests--my-c 'eval-tests--my-d)
+ :type 'cyclic-variable-indirection))
+
;;; eval-tests.el ends here