Hi,
I have simple function
(defun test()
(let ((x '((""))))
(push 1 (first x))
x))
after calling the function first time I get
((1 ""))
second time
((1 1 ""))
third time
((1 1 1 ""))
...
It works fine when '(("")) is replaced with (list (list ""))
Could you please tell me why it works so strange with quota ?
Regards, Angel
In article
<····································@e10g2000prf.googlegroups.com>,
AngelP <··········@gmail.com> wrote:
> Hi,
> I have simple function
> (defun test()
> (let ((x '((""))))
> (push 1 (first x))
> x))
>
> after calling the function first time I get
> ((1 ""))
> second time
> ((1 1 ""))
> third time
> ((1 1 1 ""))
> ...
> It works fine when '(("")) is replaced with (list (list ""))
> Could you please tell me why it works so strange with quota ?
> Regards, Angel
'(("")) is a literal constant. You are not allowed to
change it - but most compiler won't detect this
and thus they don't report an error.
This is my all-time most hated 'feature' of Common Lisp
and its implementations.
yes, that's annoying
btw. sbcl catch this
; caught WARNING:
; Destructive function SB-KERNEL:%RPLACA called on constant data.
; See also:
; The ANSI Standard, Special Operator QUOTE
; The ANSI Standard, Section 3.2.2.3
and do right thing at the end
On Feb 1, 8:49 am, Rainer Joswig <······@lisp.de> wrote:
> This is my all-time most hated 'feature' of Common Lisp
> and its implementations.
What, that modifying literals is not allowed, or that it is not
mandatory to detect it?
(The former seems to me an obviously sane restriction, while requiring
implementations to detect it would, I suspect, mean that no CL
implementation would ever conform).
On Feb 1, 4:03 pm, Tim Bradshaw <··········@tfeb.org> wrote:
> On Feb 1, 8:49 am, Rainer Joswig <······@lisp.de> wrote:
>
> > This is my all-time most hated 'feature' of Common Lisp
> > and its implementations.
>
> What, that modifying literals is not allowed, or that it is not
> mandatory to detect it?
>
> (The former seems to me an obviously sane restriction, while requiring
> implementations to detect it would, I suspect, mean that no CL
> implementation would ever conform).
You're obviously thinking static detection, like what SBCL tries to
do. I don't see why[*] literals couldn't be allocated in read-only
space and the situation detected at run time.
[*] Well, in the normal case. There are still messy cases like:
(defun foo (x) (setf (car x) 'foo))
(defparameter *data* (list 1 2 3))
(defparameter *fun*
(compile nil `(lambda (fun) (funcall fun ',*data*))))
Is that or isn't that a literal? Is (funcall *fun* #'foo) illegal but
(foo *data*) legal? Ak!
From: Kent M Pitman
Subject: Re: Why it gives me always different results
Date:
Message-ID: <usl0ccw0q.fsf@nhplace.com>
"Thomas F. Burdick" <········@gmail.com> writes:
> You're obviously thinking static detection, like what SBCL tries to
> do. I don't see why[*] literals couldn't be allocated in read-only
> space and the situation detected at run time.
>
> [*] Well, in the normal case. There are still messy cases like:
>
> (defun foo (x) (setf (car x) 'foo))
>
> (defparameter *data* (list 1 2 3))
>
> (defparameter *fun*
> (compile nil `(lambda (fun) (funcall fun ',*data*))))
Heh. You'd think it could just copy it, but then you get into the
copying problem. (A variant of the EQUAL problem I have a PS article
about.)
But yes, this is historically the messy case that has made it tricky.
Then again, before LOAD-TIME-VALUE, this was not only a messy case but an
essential capability. Since LOAD-TIME-VALUE, it's slightly less of an issue.
FWIW, MACLISP did the "worse is better" thing of having PURCOPY.
http://www.maclisp.info/pitmanual/system.html#24.31.4
Also, MACLISP kind of had a similar feature to LOAD-TIME-VALUE (well,
aimed at the same problem anyway) through its #, and SQUID [Self-Quoting
Internal Datum] facility, but they were compiler-only and did something
UTTERLY different in non-file-compiled code, so didn't have the more
uniform semantics (compiler vs. interpreter) that LOAD-TIME-VALUE has.
In article
<····································@q77g2000hsh.googlegroups.com>,
Tim Bradshaw <··········@tfeb.org> wrote:
> On Feb 1, 8:49 am, Rainer Joswig <······@lisp.de> wrote:
>
> > This is my all-time most hated 'feature' of Common Lisp
> > and its implementations.
>
> What, that modifying literals is not allowed, or that it is not
> mandatory to detect it?
The latter and that there is no way for the program
to check whether it is literal data or not.
>
> (The former seems to me an obviously sane restriction, while requiring
> implementations to detect it would, I suspect, mean that no CL
> implementation would ever conform).
On Feb 4, 5:49 pm, Rainer Joswig <······@lisp.de> wrote:
>
> The latter and that there is no way for the program
> to check whether it is literal data or not.
I think comes fairly straightforwardly into the "make it possible to
implement the language" category.
In article
<····································@k39g2000hsf.googlegroups.com>,
Tim Bradshaw <··········@tfeb.org> wrote:
> On Feb 4, 5:49 pm, Rainer Joswig <······@lisp.de> wrote:
>
> >
> > The latter and that there is no way for the program
> > to check whether it is literal data or not.
>
> I think comes fairly straightforwardly into the "make it possible to
> implement the language" category.
IMHO some more defensive language design is needed. These facilities
fall into the category of 'extremely difficult to find bugs
that can be hidden in production system for years'.
Even more so since every implementation does different things
(example: merging of constant data or not?) and a port
to another implementation can trigger bugs in an
application that was thought to run stable. I would
think that reducing variation between implementations
would be useful. Common Lisp introduces by design a lot
of things that create the possibility for bugs (like
unchecked (possibly wrong) type declarations). The language
should be more strict to reduce the number of traps to
fall into. IMHO.
For those who don't know what 'merging of constant data means':
In a file:
---
(defvar *foo* '(1 2 3 4))
(defvar *bar* '(1 2 3 4))
---
The compiler is allowed to create just ONE list and have
both variables point to the same literal data.
Some do, some don't.
On Feb 5, 11:32 am, Rainer Joswig <······@lisp.de> wrote:
> IMHO some more defensive language design is needed.
Well, it would be interesting to hear from an implementor I think.
The question really is: how hard (expensive at runtime or otherwise)
would it be to implement a check for modifying literal data on a
platform which does not have hardware-supported read-only memory? A
related question is: how hard can it be on platforms that do, if they
do it in a suitably inconvenient way?
(Given that there are at least two competing commercial
implementations, and I don't think either of them offer this, my guess
is that the answer to the second question might be "quite hard". The
answer to the first is probably mostly academic now but certainly was
not when CL was being designed.)
--tim
Tim Bradshaw <··········@tfeb.org> writes:
> On Feb 5, 11:32 am, Rainer Joswig <······@lisp.de> wrote:
>
>> IMHO some more defensive language design is needed.
>
> Well, it would be interesting to hear from an implementor I think.
> The question really is: how hard (expensive at runtime or otherwise)
> would it be to implement a check for modifying literal data on a
> platform which does not have hardware-supported read-only memory? A
> related question is: how hard can it be on platforms that do, if they
> do it in a suitably inconvenient way?
>
> (Given that there are at least two competing commercial
> implementations, and I don't think either of them offer this, my guess
> is that the answer to the second question might be "quite hard". The
> answer to the first is probably mostly academic now but certainly was
> not when CL was being designed.)
Actually, it's rather easy. On a standard Allegoro CL alisp:
CL-USER(1): (setf (schar (symbol-name 'car) 0) #\B)
Error: Attempt to store into purespace address #x17fb428.
[condition type: SIMPLE-ERROR]
Restart actions (select using :continue):
0: Return to Top Level (an "abort" restart).
1: Abort entirely from this (lisp) process.
[1] CL-USER(2):
Actually, this answer might seem disingenuous, because there are lots
of constants which _can_ be modified, even in this lisp, and I'm sure
you really meant _all_ literal data, rather than just some.
But we do put literal strings into purespace (i.e. read-only space),
and provide a way of getting literal strings into purespace, and even
declaring strings to be literal that have otherwise been
manufactured. In fact, if in this same lisp you were to do
(setq x (excl:pure-string (make-array 3 :element-type 'character
:initial-contents '(#\C #\A #\R))))
then the value of x would be eq to the prior symbol's name.
Now, let's talk about other types of literals: It might be easy enough
to see in the OP's example:
(defun test()
(let ((x '((""))))
(push 1 (first x))
x))
that the value of x is a literal that is being modified. It may even
be easy enough to see in something less obvious:
(defun test()
(let ((x '((""))))
(test1 1 x)
x))
(defun test1 (a)
(push 1 (first a)))
as long as block compilation were being done. But what about a call
chain that is not as easily traced by the compiler:
(defvar *funcs* (make-array 10))
(defun test()
(let ((x '((""))))
(funcall (aref *funcs* 0) 1 x)
x))
(defun test1 (a)
(push 1 (first a)))
Here, there is no obvious connection between the constant and the
destructive operatin performed on it, because the function is not set
in this code, but elsewhere, at some other time - there is no way for
the compiler to know what the effect is of the function being
funcalled.
In general, the only sure way to catch such settings of constants is
to pass the constant-ness around with the object and catch it at
run-time. You saw an example of that in our "purespace" strings,
whose constant-ness are given by their location in read-only memory.
This tends to be relatively cheap, though it is not trivial to set up
and it only works on objects which are supported in the purespace.
Another way to track constant-ness is to set a flag in the object, or
to give the object a different type. For a list in most CLs, the
latter would be required, since most cons cells have no extra bits.
We've toyed around with the idea of placing conses into purespace, but
in general it is hard to maintain flexibility in the purespace file
(we call these files .pll files, which stand for pure-lisp-library)
when the objects you are trying to put in have pointers
(i.e. lisp-values) in them, because they may need to be relocated when
the lisp is next started up again. We've never seriously considered
allocating a second type for cons cells.
--
Duane Rettig ·····@franz.com Franz Inc. http://www.franz.com/
555 12th St., Suite 1450 http://www.555citycenter.com/
Oakland, Ca. 94607 Phone: (510) 452-2000; Fax: (510) 452-0182
On Feb 6, 8:05 am, Duane Rettig <·····@franz.com> wrote:
>
> In general, the only sure way to catch such settings of constants is
> to pass the constant-ness around with the object and catch it at
> run-time. You saw an example of that in our "purespace" strings,
> whose constant-ness are given by their location in read-only memory.
> This tends to be relatively cheap, though it is not trivial to set up
> and it only works on objects which are supported in the purespace.
> Another way to track constant-ness is to set a flag in the object, or
> to give the object a different type. For a list in most CLs, the
> latter would be required, since most cons cells have no extra bits.
That is a good summary of what I was trying to say (better than I
could have written). Really there are three strategies I can see:
* Rely on HW protection (this is presumably what your "purespace" is -
pages mapped read-only?). Problem: well, it's HW-based so it depends
on having suitable HW (probably a good assumption now, but was it
always?), a decent interface to it (likewise), and it being adequately
cheap (so presumably not a huge number of tiny mappings, nor
repeatedly toggling the RO-ness of a page as you fill it with stuff).
* An extra tag bit which you check. Well, that's one of a rather small
number of bits with no easy way to create more (The historical "OK,
we'll just increase the word length of our machine by a few bits"
answer did not turn out to be enormously successful). Do
implementations typically have *any* free tag bits (and if so, why)?
* Emulating HW protection by keeping track of constant pages. I'd
worry about the extra memory traffic this is likely to generate
(potentially an extra fetch per object, I think), but may be it would
all get cached.
And I suppose the fourth way is defining read-only variants of every
type. For things whose type is in the tag (conses? strings?) this
must be mostly the same as the extra tag-bit approach mustn't it?
To answer a bit of your message that I elided: yes, I think that if
the language were to mandate it then it needs to hold for all types.
If the language does not mandate it, then it becomes a quality-of-
implementation issue, with high-quality implementations (like ACL)
being able to support it for some types, some times.
Tim Bradshaw <··········@tfeb.org> writes:
> * An extra tag bit which you check. Well, that's one of a rather small
> number of bits with no easy way to create more (The historical "OK,
> we'll just increase the word length of our machine by a few bits"
> answer did not turn out to be enormously successful). Do
> implementations typically have *any* free tag bits (and if so, why)?
Doesn't the move to 64 bits change this? If I were thinking
about designing a new language I would be tempted to say it
was 64 bit only, with 48 bit addresses and the luxury of 16
tag bits.
Alan Crowe
Edinburgh
Scotland
On Fri, 08 Feb 2008 12:30:06 +0000, Alan Crowe wrote:
> Tim Bradshaw <··········@tfeb.org> writes:
>
>> * An extra tag bit which you check. Well, that's one of a rather small
>> number of bits with no easy way to create more (The historical "OK,
>> we'll just increase the word length of our machine by a few bits"
>> answer did not turn out to be enormously successful). Do
>> implementations typically have *any* free tag bits (and if so, why)?
>
> Doesn't the move to 64 bits change this? If I were thinking about
> designing a new language I would be tempted to say it was 64 bit only,
> with 48 bit addresses and the luxury of 16 tag bits.
>
> Alan Crowe
> Edinburgh
> Scotland
Hmmmm... we are using about 36 bits now in large systems, and memory sizes
are going up by roughly 1 bit per 12 months. So 48 bits will run out in
only 12 years. Which is a lot closer than 2000 AD was when all that
non-Y2K compatible code was written.
If instead we only used 2 bits for tags, this leaves 62 bits for fixnum.
The bigger fixnum is the better IMHO.
I remember reading an article in Byte magazine where the author complained
that the MC68000 chip made it hard to use the 'spare' 8 bits as tag bits.
At the time the 68000 had 32 bit registers and 24 bit addressing.
Of course lisp makes conversion easier because the internal representation
is transparent to the programmer.
Tim Josling
tim <····@internet.com> wrote:
+---------------
| Alan Crowe wrote:
| > Tim Bradshaw <··········@tfeb.org> writes:
| >> Do implementations typically have *any* free tag bits (and if so, why)?
| >
| > Doesn't the move to 64 bits change this? If I were thinking about
| > designing a new language I would be tempted to say it was 64 bit only,
| > with 48 bit addresses and the luxury of 16 tag bits.
|
| Hmmmm... we are using about 36 bits now in large systems, and memory sizes
| are going up by roughly 1 bit per 12 months. So 48 bits will run out in
| only 12 years.
+---------------
Sooner than that. Large SGI Altix systems already need 44 physical
address bits to be able to address main system RAM (yes, they can
put ~14 TB of global cache-coherent RAM on their largest ccNUMA boxes).
-Rob
-----
Rob Warnock <····@rpw3.org>
627 26th Avenue <URL:http://rpw3.org/>
San Mateo, CA 94403 (650)572-2607
On Fri, 08 Feb 2008 21:26:06 -0000, tim <····@internet.com> wrote:
>I remember reading an article in Byte magazine where the author complained
>that the MC68000 chip made it hard to use the 'spare' 8 bits as tag bits.
>At the time the 68000 had 32 bit registers and 24 bit addressing.
The 68000 had separate address and data registers. Address registers
had only dereference and autoincrement ops. Any other manipulation on
pointer data had to be done using data registers.
The 68000 ignored the top 8 bits of the pointer so initially it wasn't
necessary to strip tags. But when the 68020 and later chips used the
full 32-bit address space, tags had to be stripped before using the
pointer. Stripping tags required at least one data register (possibly
2) and 3-4 instructions depending on exactly how it was accomplished.
And the 68K was register starved by today's standards ... only 8
registers of each type. So yeah ... tagged pointers were somewhat of
a pain.
George
--
for email reply remove "/" from address
Tim Bradshaw <··········@tfeb.org> writes:
> On Feb 5, 11:32 am, Rainer Joswig <······@lisp.de> wrote:
>
>> IMHO some more defensive language design is needed.
>
> Well, it would be interesting to hear from an implementor I think.
> The question really is: how hard (expensive at runtime or otherwise)
> would it be to implement a check for modifying literal data on a
> platform which does not have hardware-supported read-only memory? A
> related question is: how hard can it be on platforms that do, if they
> do it in a suitably inconvenient way?
>
> (Given that there are at least two competing commercial
> implementations, and I don't think either of them offer this, my guess
> is that the answer to the second question might be "quite hard". The
> answer to the first is probably mostly academic now but certainly was
> not when CL was being designed.)
If you would consider only cons cells, it's rather easy: you need to
add just one bit per cons cell, and one test in each rplaca and
rplacd. It's not much more complex to handle the other literal
structures either.
In addition, you would have to transport this bit over fasl files.
On platform with hardware support, it would be harder,
implementationwise (you'd have to allocate literal cells in read-only
pages, meaning unlock the page, allocate, relock the page, and then
handle traps), but it could be more efficient, since rplaca and rplacd
wouldn't have to test anything, relying on hardware traps.
IMHO, it such a trivial feature that it could be added to any
implementation, for example for (safety 3) or (debug 3). On the other
hand, I don't think that not checking for literal mutation is needed
at all: it gives a good rite of passage for newbies. But then, I also
learned to program in assembler soon...
--
__Pascal Bourguignon__ http://www.informatimago.com/
PLEASE NOTE: Some quantum physics theories suggest that when the
consumer is not directly observing this product, it may cease to
exist or will exist only in a vague and undetermined state.
Pascal Bourguignon <···@informatimago.com> writes:
> If you would consider only cons cells, it's rather easy: you need to
> add just one bit per cons cell, and one test in each rplaca and
> rplacd. It's not much more complex to handle the other literal
> structures either.
You don't need to have a bit per object; you can have a bit per page.
The allocator would allocate read only objects on read only pages.
Each page would start with a header, which records whether it is a
read only page or not.
Pages would be allocated on known alignments, so that if you have an
object you can find the page header for that object using only simple
address arithmetic, likely masking off a couple of bits.
rplaca and friends would need to perform the address arithmetic and
check the page wide read only bit.
-russ
Russell McManus <···············@yahoo.com> writes:
> Pascal Bourguignon <···@informatimago.com> writes:
>
>> If you would consider only cons cells, it's rather easy: you need to
>> add just one bit per cons cell, and one test in each rplaca and
>> rplacd. It's not much more complex to handle the other literal
>> structures either.
>
> You don't need to have a bit per object; you can have a bit per page.
>
> The allocator would allocate read only objects on read only pages.
Minor point: depending on what you mean by "read-only" page, if you
take this to mean that something is enforced by hardware, then you
wouldn't allocate the object into the page _while_ it is read-only;
the page attributes would have to be changed to allow writing by the
allocator, and then changed back. Of course, you could just
"designate" pages as read-only, but then all destructive functions
would have to cooperate...
> Each page would start with a header, which records whether it is a
> read only page or not.
>
> Pages would be allocated on known alignments, so that if you have an
> object you can find the page header for that object using only simple
> address arithmetic, likely masking off a couple of bits.
>
> rplaca and friends would need to perform the address arithmetic and
> check the page wide read only bit.
... but if the page were set up as read-only enforced by the
hardware, then no checks would be necessary; the access itself would
be enough to cause a trap, which can then be analyzed by the trap
handler (see my earlier response to Tim).
--
Duane Rettig ·····@franz.com Franz Inc. http://www.franz.com/
555 12th St., Suite 1450 http://www.555citycenter.com/
Oakland, Ca. 94607 Phone: (510) 452-2000; Fax: (510) 452-0182
Duane Rettig <·····@franz.com> writes:
> Minor point: depending on what you mean by "read-only" page, if you
> take this to mean that something is enforced by hardware, then you
> wouldn't allocate the object into the page _while_ it is read-only;
> the page attributes would have to be changed to allow writing by the
> allocator, and then changed back. Of course, you could just
> "designate" pages as read-only, but then all destructive functions
> would have to cooperate...
Thanks for the clarification. I was referring (confusingly) to the
cooperation strategy, not the hardware support strategy.
Both strategies work, of course. I wanted to make the point that even
if you don't have hardware support, you can still get by without a
per-object read-only bit.
-russ
Duane Rettig wrote:
> Russell McManus <···············@yahoo.com> writes:
>
>> Pascal Bourguignon <···@informatimago.com> writes:
>>
>>> If you would consider only cons cells, it's rather easy: you need to
>>> add just one bit per cons cell, and one test in each rplaca and
>>> rplacd. It's not much more complex to handle the other literal
>>> structures either.
>> You don't need to have a bit per object; you can have a bit per page.
>>
>> The allocator would allocate read only objects on read only pages.
>
> Minor point: depending on what you mean by "read-only" page, if you
> take this to mean that something is enforced by hardware, then you
> wouldn't allocate the object into the page _while_ it is read-only;
> the page attributes would have to be changed to allow writing by the
> allocator, and then changed back. Of course, you could just
> "designate" pages as read-only, but then all destructive functions
> would have to cooperate...
Off the top of my head, but with a generational GC that stops all
threads while it runs, you could have "pseudo read-only" pages in the
nursery and then arrange that when the objects in them are promoted to a
higher generation they go into pages destined to be hardware-enforced
read-only. The GC then applies hardware protections as its last action
before resuming mutation.
(If you're using write-protect bits already to trap mutations in older
generations, this still works but obviously you have an extra case in
the trap handler)
Writes to fresh read-only objects would still not be trapped, but you'd
catch any attempts that happened after the first GC. Not a 100%
solution, and you'd have to determine whether the percentage it actually
does catch warrants the extra complexity. It would certainly pick up
some of the occasions I've burnt myself writing to a quoted list,
because it's usually only the second time I run the function that I
notice it started with different state from the first time.
-dan
On Feb 6, 3:44 pm, ····@telent.net wrote:
>
> Off the top of my head, but with a generational GC that stops all
> threads while it runs, you could have "pseudo read-only" pages in the
> nursery and then arrange that when the objects in them are promoted to a
> higher generation they go into pages destined to be hardware-enforced
> read-only. The GC then applies hardware protections as its last action
> before resuming mutation.
This kind of thing is exactly what I meant by not requiring either
special HW support or heroic implementation strategies!