GNU bug report logs - #70368
[PATCH] Use a dedicated type to represent interpreted-function values

Please note: This is a static page, with minimal formatting, updated once a day.
Click here to see this page with the latest information and nicer formatting.

Package: emacs; Reported by: Stefan Monnier <monnier@HIDDEN>; Keywords: patch; dated Sat, 13 Apr 2024 19:58:03 UTC; Maintainer for emacs is bug-gnu-emacs@HIDDEN.

Message received at 70368 <at> debbugs.gnu.org:


Received: (at 70368) by debbugs.gnu.org; 15 Apr 2024 11:24:50 +0000
From debbugs-submit-bounces <at> debbugs.gnu.org Mon Apr 15 07:24:48 2024
Received: from localhost ([127.0.0.1]:36979 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1rwKRz-0001l9-RM
	for submit <at> debbugs.gnu.org; Mon, 15 Apr 2024 07:24:48 -0400
Received: from eggs.gnu.org ([2001:470:142:3::10]:35612)
 by debbugs.gnu.org with esmtp (Exim 4.84_2)
 (envelope-from <eliz@HIDDEN>) id 1rwKRn-0001hi-92
 for 70368 <at> debbugs.gnu.org; Mon, 15 Apr 2024 07:24:36 -0400
Received: from fencepost.gnu.org ([2001:470:142:3::e])
 by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.90_1) (envelope-from <eliz@HIDDEN>)
 id 1rwKRV-000245-Bw; Mon, 15 Apr 2024 07:24:09 -0400
DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org;
 s=fencepost-gnu-org; h=References:Subject:In-Reply-To:To:From:Date:
 mime-version; bh=lf53TieTK7Zti3NYTU2uKkyXC14+LfJc9XIBf7BD+2c=; b=gXtxqSXx6VSE
 QM/+pz3SwnWvpvJ8xCSR60iMECzDr9RnqBOIucz1OK6gPxgUyAPt+mU78H81HcF5ODxGfKi4rv2X8
 ofj6IamWrAA7HGL5RdmlMgaZ65NqyOcMMdlMYt97N9loS+Uez+7oHv0aAhYvfXsJSSBsROhRqvm0u
 irM+CL64JmLuV2hykV3FBSKuuuZvWgBC3mO2kGxYp57BshLtqHNoOCwR13k3zWuxOigUtMC6zwuE6
 rP4xYpbrjdCudGP+/uimrKhxe38EfKLP3/2tEA8xmpJ4gQjVqaKvE9K5R2pE246OPsl2Uh9jsiIIa
 y/39jONgYRuq/d3ircwkUg==;
Date: Mon, 15 Apr 2024 14:23:58 +0300
Message-Id: <86sezmlvht.fsf@HIDDEN>
From: Eli Zaretskii <eliz@HIDDEN>
To: Stefan Monnier <monnier@HIDDEN>
In-Reply-To: <jwvil0jvahm.fsf-monnier+emacs@HIDDEN> (message from Stefan
 Monnier on Sun, 14 Apr 2024 19:03:57 -0400)
Subject: Re: bug#70368: [PATCH] Use a dedicated type to represent
 interpreted-function values
References: <jwvo7adxcsp.fsf-monnier+@gnu.org> <86il0ko6f9.fsf@HIDDEN>
 <jwvo7acw226.fsf-monnier+emacs@HIDDEN> <86h6g4m29n.fsf@HIDDEN>
 <jwvil0jvahm.fsf-monnier+emacs@HIDDEN>
X-Spam-Score: -2.3 (--)
X-Debbugs-Envelope-To: 70368
Cc: 70368 <at> debbugs.gnu.org
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -3.3 (---)

> From: Stefan Monnier <monnier@HIDDEN>
> Cc: 70368 <at> debbugs.gnu.org
> Date: Sun, 14 Apr 2024 19:03:57 -0400
> 
> >> - "soft" incompatibilities for code which tries to display the kind of
> >>   the object it receives (such as in a completion table that wants to
> >>   add an icon indicating if something is a macro, a compiled function,
> >>   etc...).  Such code will still work but may display less informative
> >>   info because it may fail to recognize the new objects as being
> >>   interpreted functions.
> >> - "real" incompatibilities for code which digs inside the
> >>   entrails of functions to try and extract specific information.
> >>   This may fail when faced with the new interpreted functions
> >>   but should be easy to fix.
> >>   As long as such code only tries to extract info and does it via
> >>   `help-function-arglist`, `documentation`, and `interactive-form`,
> >>   there's no problem, but some packages may use code inherited from
> >>   a long time ago when `help-function-arglist` didn't exist, or written
> >>   by coders who didn't know better.  I know Hyperbole used to do that,
> >>   but I believe that's been fixed since.
> >> - "hard" incompatibilities for code which really digs inside the
> >>   code of functions.  `vc.el` did that a long time ago,
> >>   `kmacro.el` did as well until OClosures, and Buttercup (a NonGNU ELPA
> >>   package) did until a few months ago.  There are probably one or two
> >>   packages out there that will be affected like Buttercup would have
> >>   been.  FWIW, the Buttercup case was a mix of "hard" and "soft": it
> >>   really looked inside the code, but used that only to provide more
> >>   informative messages and had a fallback case when the code was
> >>   compiled, so it would still work OK.
> >
> > Some of the above should be in NEWS, I think, in the "incompatible
> > Lisp changes" section.
> 
> I don't really know what more I could put in there.  The entry I added
> states the actual changes and the above "two" are just
> direct consequences.

I think the only problems we need to mention are those with Lisp
programs that "dig" inside the code of functions.  I think it should
be enough to give one or two concrete examples (what internals the
offending code depended on), and tell that these techniques will no
longer work.  You mention above examples of packages that used to do
it, but that's not useful; providing specific examples of what those
packages did will explain the problems much better.

> How do you imagine a user or developer is going to make use of the
> above info?

They will realize which techniques are no longer supposed to work, and
could look into their code to try find such techniques.

> > I think this also calls for augmenting the documentation of
> > make-byte-code to the effect that it could now create closures as
> > well.
> 
> I'm not very happy documenting it, because I think it's an
> implementation accident resulting from the historical evolution of
> the code.  IOW something we may want to fix.

Then maybe a defalias with a suitable name could be the first step in
that direction?

I suggested documenting it because the name of the function no longer
describes accurately what it does, and people might be surprised to
see that it is used to create something other than byte-code.




Information forwarded to bug-gnu-emacs@HIDDEN:
bug#70368; Package emacs. Full text available.

Message received at 70368 <at> debbugs.gnu.org:


Received: (at 70368) by debbugs.gnu.org; 14 Apr 2024 23:04:23 +0000
From debbugs-submit-bounces <at> debbugs.gnu.org Sun Apr 14 19:04:23 2024
Received: from localhost ([127.0.0.1]:36370 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1rw8tY-0001mC-1H
	for submit <at> debbugs.gnu.org; Sun, 14 Apr 2024 19:04:23 -0400
Received: from mailscanner.iro.umontreal.ca ([132.204.25.50]:35590)
 by debbugs.gnu.org with esmtp (Exim 4.84_2)
 (envelope-from <monnier@HIDDEN>) id 1rw8tV-0001kl-22
 for 70368 <at> debbugs.gnu.org; Sun, 14 Apr 2024 19:04:18 -0400
Received: from pmg1.iro.umontreal.ca (localhost.localdomain [127.0.0.1])
 by pmg1.iro.umontreal.ca (Proxmox) with ESMTP id CD5F310004C;
 Sun, 14 Apr 2024 19:03:59 -0400 (EDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=iro.umontreal.ca;
 s=mail; t=1713135838;
 bh=NZc7tZ3xlUhiRKHZvZ3qzALk2BxpLPeUlOsUdLSVgVI=;
 h=From:To:Cc:Subject:In-Reply-To:References:Date:From;
 b=IE+DKoXjvu78NeLIWLd9w9j7Ix8MeDXwhQkR8ATp2WA2DTS5DcA8b7mbHjaNPZcwf
 EupibgSEmBnzTRl95Gdjbg9g1f4kzdlpCsWiQDdVvq6mpTYlKuEhd/DBvuOrE+uaCA
 KA7l/cvfvvaDue5IuAphjUzRtjiDoS7pp/XltMVoInrd3ksRQVvMi+hYwoUkIk3X18
 e3o4I6PIM9FjlN6ODgnNSwQJRaGLrCuYYHBMYYGWbxxz1E3NJncmVq0soOxOGOSlX1
 fyPLgxYa/YZ60FFycSb75X+2UBw6TXG3SOLo4h9+uXRhheAv7z9RFIo8wPh0fbmDd8
 9+Fst+6/Apo5Q==
Received: from mail01.iro.umontreal.ca (unknown [172.31.2.1])
 by pmg1.iro.umontreal.ca (Proxmox) with ESMTP id AA9C8100048;
 Sun, 14 Apr 2024 19:03:58 -0400 (EDT)
Received: from pastel (unknown [45.72.201.215])
 by mail01.iro.umontreal.ca (Postfix) with ESMTPSA id 68146120115;
 Sun, 14 Apr 2024 19:03:58 -0400 (EDT)
From: Stefan Monnier <monnier@HIDDEN>
To: Eli Zaretskii <eliz@HIDDEN>
Subject: Re: bug#70368: [PATCH] Use a dedicated type to represent
 interpreted-function values
In-Reply-To: <86h6g4m29n.fsf@HIDDEN> (Eli Zaretskii's message of "Sun, 14 Apr
 2024 17:45:24 +0300")
Message-ID: <jwvil0jvahm.fsf-monnier+emacs@HIDDEN>
References: <jwvo7adxcsp.fsf-monnier+@gnu.org> <86il0ko6f9.fsf@HIDDEN>
 <jwvo7acw226.fsf-monnier+emacs@HIDDEN> <86h6g4m29n.fsf@HIDDEN>
Date: Sun, 14 Apr 2024 19:03:57 -0400
User-Agent: Gnus/5.13 (Gnus v5.13)
MIME-Version: 1.0
Content-Type: text/plain
X-SPAM-INFO: Spam detection results:  0
 ALL_TRUSTED                -1 Passed through trusted hosts only via SMTP
 AWL -0.026 Adjusted score from AWL reputation of From: address
 BAYES_00                 -1.9 Bayes spam probability is 0 to 1%
 DKIM_SIGNED               0.1 Message has a DKIM or DK signature,
 not necessarily valid
 DKIM_VALID -0.1 Message has at least one valid DKIM or DK signature
 DKIM_VALID_AU -0.1 Message has a valid DKIM or DK signature from author's
 domain
 DKIM_VALID_EF -0.1 Message has a valid DKIM or DK signature from envelope-from
 domain
X-SPAM-LEVEL: 
X-Spam-Score: -2.3 (--)
X-Debbugs-Envelope-To: 70368
Cc: 70368 <at> debbugs.gnu.org
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -3.3 (---)

>> There are two kinds of incompatibilities it introduces, in my experience:
>> 
>> - "soft" incompatibilities for code which tries to display the kind of
>>   the object it receives (such as in a completion table that wants to
>>   add an icon indicating if something is a macro, a compiled function,
>>   etc...).  Such code will still work but may display less informative
>>   info because it may fail to recognize the new objects as being
>>   interpreted functions.
>> - "real" incompatibilities for code which digs inside the
>>   entrails of functions to try and extract specific information.
>>   This may fail when faced with the new interpreted functions
>>   but should be easy to fix.
>>   As long as such code only tries to extract info and does it via
>>   `help-function-arglist`, `documentation`, and `interactive-form`,
>>   there's no problem, but some packages may use code inherited from
>>   a long time ago when `help-function-arglist` didn't exist, or written
>>   by coders who didn't know better.  I know Hyperbole used to do that,
>>   but I believe that's been fixed since.
>> - "hard" incompatibilities for code which really digs inside the
>>   code of functions.  `vc.el` did that a long time ago,
>>   `kmacro.el` did as well until OClosures, and Buttercup (a NonGNU ELPA
>>   package) did until a few months ago.  There are probably one or two
>>   packages out there that will be affected like Buttercup would have
>>   been.  FWIW, the Buttercup case was a mix of "hard" and "soft": it
>>   really looked inside the code, but used that only to provide more
>>   informative messages and had a fallback case when the code was
>>   compiled, so it would still work OK.
>
> Some of the above should be in NEWS, I think, in the "incompatible
> Lisp changes" section.

I don't really know what more I could put in there.  The entry I added
states the actual changes and the above "two" are just
direct consequences.

Obviously I could copy&paste that text literally, but it seems to me
it'd just make etc/NEWS longer for no concrete benefit.  Or maybe I'm
just misunderstanding.  How do you imagine a user or developer is going
to make use of the above info?

>> Where would that be?  I don't see where we document the
>> #f(compiled-function ...) used for byte-code, which was my inspiration
>> for the #f(lambda ...).
>
> We have the "Printed Representation" node, and then the representation
> of each type is documented where the type is documented, in
> subsections of "Programming Types".  So somewhere there, I think,
> probably in "Function Type"?  And you probably want to review
> "Byte-Code Type" as well.

AFAICT we currently don't document the cl-print output in there.
I did (in the mean time) update the "Byte-Code Type" to clarify that the
printed representation is the same #[...] as byte codes, and I also
mention the #f(...) notation.

>> This should be an extremely rare occurrence, and `display-call-tree`
>> itself is not documented, so I doubt it's worth the trouble.
>> If you prefer I can keep it as `<function>`.
> I thing <function> is preferable, since <function-like list> is
> somewhat mysterious, and what you say above means explaining the fine
> differences will be largely a wasted effort.

OK.

>> >> +DEFUN ("closurep", Fclosurep, Sclosurep,
>> >> +       1, 1, 0,
>> >> +       doc: /* Return t if OBJECT is a function object.  */)
>> >
>> > If the doc string is correct, then why is the function called
>> > 'closurep'?  It's against mnemonic memory.
>> 
>> The term "closure" is kind of funny, indeed: often it's used to say
>> "this is a piece of code packaged with its environment", but in most
>> cases where it's used all functions are like that, so the precise
>> meaning of "closure" can be argued to be exactly the same as "function",
>> just with a different connotation.  Sometimes the connotation is to
>> insist on the fact that it captured some variables, but object it's used
>> to distinguish between the abstract notion of functions and their
>> runtime representation, where "closure" means "an object which
>> represents a function".
>> 
>> IOW, the docstring above is meant to say "a function *object*" rather
>> than "a *function* object".  Compare with `functionp`:
>> 
>>     Return t if OBJECT is a function.
>> 
>> Maybe I should say something like:
>> 
>>     Return t if OBJECT is a closure, i.e. a function object.
>> 
>> ?
>
> Yes, I think that'd be better.

In the end, I went with:

       doc: /* Return t if OBJECT is a function of type `closure'.  */)

and improved the docstring of the `closure` type, so we get a more
direct access to a short description of the various fields of byte-code
and interpreted-function objects.

> The problem is that "function value" can be interpreted as "value
> returned by a function".

Ah, right.  Thanks.

>> Because `make-byte-code` is the function that we already have which
>> creates PVEC_CLOSURE (previous called PVEC_COMPILED) objects.  It used
>> to be used exclusively to create byte-code functions but now that same
>> representation is used for interpreted function.  I could rename it but
>> we already have `make-closure` for something slightly different, and
>> I don't see much benefit to the rename.
>> I'll add a comment explaining why we use `Fmake_byte_code`,
>
> I think this also calls for augmenting the documentation of
> make-byte-code to the effect that it could now create closures as
> well.

I'm not very happy documenting it, because I think it's an
implementation accident resulting from the historical evolution of
the code.  IOW something we may want to fix.
People who want to create an interpreted function should use
`make-interpreted-function` rather than `make-byte-code`.


        Stefan





Information forwarded to bug-gnu-emacs@HIDDEN:
bug#70368; Package emacs. Full text available.

Message received at 70368 <at> debbugs.gnu.org:


Received: (at 70368) by debbugs.gnu.org; 14 Apr 2024 19:11:31 +0000
From debbugs-submit-bounces <at> debbugs.gnu.org Sun Apr 14 15:11:31 2024
Received: from localhost ([127.0.0.1]:36167 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1rw5GF-0002rt-Hl
	for submit <at> debbugs.gnu.org; Sun, 14 Apr 2024 15:11:31 -0400
Received: from eggs.gnu.org ([2001:470:142:3::10]:47464)
 by debbugs.gnu.org with esmtp (Exim 4.84_2)
 (envelope-from <eliz@HIDDEN>) id 1rw5GB-0002qD-GK
 for 70368 <at> debbugs.gnu.org; Sun, 14 Apr 2024 15:11:28 -0400
Received: from fencepost.gnu.org ([2001:470:142:3::e])
 by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.90_1) (envelope-from <eliz@HIDDEN>)
 id 1rw5Fu-000694-Ug; Sun, 14 Apr 2024 15:11:10 -0400
DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org;
 s=fencepost-gnu-org; h=References:Subject:In-Reply-To:To:From:Date:
 mime-version; bh=t1EVzgE49Mpb0+aWIjiwMgs08YAenIULP+J6RGJ1FAk=; b=HU/opstB1lvF
 nvmFv29HIctW8zhy/qT1KcZYVa273JFkYlDwsrh8djxWq7aF176KkQiVaKOLie1eSu5MweBH+cD0e
 9PJaYyyIPwyompUD1ioitr//g6n/FBw1t1rNx6t1mf9MBJ9ZTY5tQ5tTIT2Pg+W7g+iwoIHDzfNhJ
 E92WktXb1oWYsONTZPJg+zgKvHwyX3xPF51qU882a1BEznhHNCf8M7v25vy/3RuqY4T0yiHvhd1K+
 k3NKMfwQ6g1b4ssjMjIswsbkVAUPHUAf+1KmCgbDe1W/05cBV21P46Rs24glr1DuquCTI0EHY0+kU
 JWSRv8oDC0F5sXd+gW9Ajw==;
Date: Sun, 14 Apr 2024 22:11:05 +0300
Message-Id: <8634rnn4ja.fsf@HIDDEN>
From: Eli Zaretskii <eliz@HIDDEN>
To: Stefan Monnier <monnier@HIDDEN>
In-Reply-To: <jwv8r1fx6n2.fsf-monnier+emacs@HIDDEN> (message from Stefan
 Monnier on Sun, 14 Apr 2024 12:18:24 -0400)
Subject: Re: bug#70368: [PATCH] Use a dedicated type to represent
 interpreted-function values
References: <jwvo7adxcsp.fsf-monnier+@gnu.org> <86il0ko6f9.fsf@HIDDEN>
 <jwvo7acw226.fsf-monnier+emacs@HIDDEN>
 <jwv8r1fx6n2.fsf-monnier+emacs@HIDDEN>
X-Spam-Score: -2.3 (--)
X-Debbugs-Envelope-To: 70368
Cc: 70368 <at> debbugs.gnu.org
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -3.3 (---)

> From: Stefan Monnier <monnier@HIDDEN>
> Cc: 70368 <at> debbugs.gnu.org
> Date: Sun, 14 Apr 2024 12:18:24 -0400
> 
> Here's the corresponding additional patch (not yet merged into the main patch).

Thanks.

> +For interpreted functions, this is nil if the function is using the old
                                      ^^^
@code{nil}

> +@defun make-interpreted-closure args body env &optional docstring iform
> +This function constructs and returns a closure representing the
> +interpreted function with arguments @var{args} and whose body is made of
> +@var{body} which must be a non-nil list of Lisp forms.  @var{env} is the
                                  ^^^
@code{nil}

> +lexical environment in the same form as used with @code{eval}
> +(@pxref{Eval}).  The documentation @var{docstring} if non-nil should be
                                                             ^^^
Likewise.

> +a string, and the interactive form @var{iform} if non-nil should be of
                                                         ^^^
And likewise.

> +the form @code{(interactive @var{arg-descriptor})} (@pxref{Using
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This should be in @w{..}, to prevent it from being broken across
lines.




Information forwarded to bug-gnu-emacs@HIDDEN:
bug#70368; Package emacs. Full text available.

Message received at 70368 <at> debbugs.gnu.org:


Received: (at 70368) by debbugs.gnu.org; 14 Apr 2024 16:18:50 +0000
From debbugs-submit-bounces <at> debbugs.gnu.org Sun Apr 14 12:18:50 2024
Received: from localhost ([127.0.0.1]:36024 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1rw2Z5-0007gl-5P
	for submit <at> debbugs.gnu.org; Sun, 14 Apr 2024 12:18:50 -0400
Received: from mailscanner.iro.umontreal.ca ([132.204.25.50]:30533)
 by debbugs.gnu.org with esmtp (Exim 4.84_2)
 (envelope-from <monnier@HIDDEN>) id 1rw2Z2-0007fV-Ov
 for 70368 <at> debbugs.gnu.org; Sun, 14 Apr 2024 12:18:45 -0400
Received: from pmg1.iro.umontreal.ca (localhost.localdomain [127.0.0.1])
 by pmg1.iro.umontreal.ca (Proxmox) with ESMTP id DF3B410004C;
 Sun, 14 Apr 2024 12:18:27 -0400 (EDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=iro.umontreal.ca;
 s=mail; t=1713111506;
 bh=FSu4I8HlXP9fr8YoPBMWPgUdYTUBIuCyAi5EtcgcqO0=;
 h=From:To:Cc:Subject:In-Reply-To:References:Date:From;
 b=KfjeEZrgLzIzHuNqscLFNCF0l7LwiXqZtQFOR3TJHYW6Mfu4O+w2opj7O24rb3RjH
 K77v5GKEuT0/wkCRbqHC18Xaz8NgV3M6hFtwNvI+7Vz9/D5bgtrhAiR2T64v7wEo/i
 aGlaIp7t4bYW08gbirbBgqnS8t+uJtbuQWjx9TjIGNELruqNTZxz/lIWxxgUlm25pt
 nvV3vyZjSjv/3a4YOH4RwvBpKEk4l+Sc1o+I4WpvXpNXli6z8ZBJ2RkMt9eFfZ6PSH
 C4LMQTji926Ic1XjSsFq0FNSka9QmpcoSX9jsT6SwrybE+sr4d6wbP5ufI3LF12BNn
 J1vtQwwUAa3GA==
Received: from mail01.iro.umontreal.ca (unknown [172.31.2.1])
 by pmg1.iro.umontreal.ca (Proxmox) with ESMTP id 2DB2F100048;
 Sun, 14 Apr 2024 12:18:26 -0400 (EDT)
Received: from pastel (unknown [45.72.201.215])
 by mail01.iro.umontreal.ca (Postfix) with ESMTPSA id 091AE1201E7;
 Sun, 14 Apr 2024 12:18:26 -0400 (EDT)
From: Stefan Monnier <monnier@HIDDEN>
To: Eli Zaretskii <eliz@HIDDEN>
Subject: Re: bug#70368: [PATCH] Use a dedicated type to represent
 interpreted-function values
In-Reply-To: <jwvo7acw226.fsf-monnier+emacs@HIDDEN> (Stefan Monnier's message
 of "Sun, 14 Apr 2024 09:49:23 -0400")
Message-ID: <jwv8r1fx6n2.fsf-monnier+emacs@HIDDEN>
References: <jwvo7adxcsp.fsf-monnier+@gnu.org> <86il0ko6f9.fsf@HIDDEN>
 <jwvo7acw226.fsf-monnier+emacs@HIDDEN>
Date: Sun, 14 Apr 2024 12:18:24 -0400
User-Agent: Gnus/5.13 (Gnus v5.13)
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="=-=-="
X-SPAM-INFO: Spam detection results:  0
 ALL_TRUSTED                -1 Passed through trusted hosts only via SMTP
 AWL -0.026 Adjusted score from AWL reputation of From: address
 BAYES_00                 -1.9 Bayes spam probability is 0 to 1%
 DKIM_SIGNED               0.1 Message has a DKIM or DK signature,
 not necessarily valid
 DKIM_VALID -0.1 Message has at least one valid DKIM or DK signature
 DKIM_VALID_AU -0.1 Message has a valid DKIM or DK signature from author's
 domain
 DKIM_VALID_EF -0.1 Message has a valid DKIM or DK signature from envelope-from
 domain
X-SPAM-LEVEL: 
X-Spam-Score: -2.3 (--)
X-Debbugs-Envelope-To: 70368
Cc: 70368 <at> debbugs.gnu.org
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -3.3 (---)

--=-=-=
Content-Type: text/plain

Here's the corresponding additional patch (not yet merged into the main patch).


        Stefan

--=-=-=
Content-Type: text/x-diff
Content-Disposition: inline; filename=interpreted-function.diff

diff --git a/doc/lispref/compile.texi b/doc/lispref/compile.texi
index 00602198da5..b5f3b07f083 100644
--- a/doc/lispref/compile.texi
+++ b/doc/lispref/compile.texi
@@ -37,7 +37,7 @@ Byte Compilation
 * Docs and Compilation::        Dynamic loading of documentation strings.
 * Eval During Compile::         Code to be evaluated when you compile.
 * Compiler Errors::             Handling compiler error messages.
-* Byte-Code Objects::           The data type used for byte-compiled functions.
+* Closure Objects::             The data type used for byte-compiled functions.
 * Disassembly::                 Disassembling byte-code; how to read byte-code.
 @end menu
 
@@ -120,7 +120,7 @@ Compilation Functions
 definition of @var{symbol} must be the actual code for the function;
 @code{byte-compile} does not handle function indirection.  The return
 value is the byte-code function object which is the compiled
-definition of @var{symbol} (@pxref{Byte-Code Objects}).
+definition of @var{symbol} (@pxref{Closure Objects}).
 
 @example
 @group
@@ -487,21 +487,22 @@ Compiler Errors
 using @code{error}.  If so, set @code{byte-compile-error-on-warn} to a
 non-@code{nil} value.
 
-@node Byte-Code Objects
-@section Byte-Code Function Objects
+@node Closure Objects
+@section Closure Function Objects
 @cindex compiled function
 @cindex byte-code function
 @cindex byte-code object
 
-  Byte-compiled functions have a special data type: they are
-@dfn{byte-code function objects}.  Whenever such an object appears as
-a function to be called, Emacs uses the byte-code interpreter to
-execute the byte-code.
+  Byte-compiled functions use a special data type: they are closures.
+Closures are used both for byte-compiled Lisp functions as well as for
+interpreted Lisp functions.  Whenever such an object appears as
+a function to be called, Emacs uses the appropriate interpreter to
+execute either the byte-code or the non-compiled Lisp code.
 
-  Internally, a byte-code function object is much like a vector; its
+  Internally, a closure is much like a vector; its
 elements can be accessed using @code{aref}.  Its printed
 representation is like that for a vector, with an additional @samp{#}
-before the opening @samp{[}.  It must have at least four elements;
+before the opening @samp{[}.  It must have at least three elements;
 there is no maximum number, but only the first six elements have any
 normal use.  They are:
 
@@ -515,20 +516,28 @@ Byte-Code Objects
 the argument list uses @code{&rest}, then bit 7 is set; otherwise it's
 cleared.
 
-If @var{argdesc} is a list, the arguments will be dynamically bound
+When the closure is a byte-code function,
+if @var{argdesc} is a list, the arguments will be dynamically bound
 before executing the byte code.  If @var{argdesc} is an integer, the
 arguments will be instead pushed onto the stack of the byte-code
 interpreter, before executing the code.
 
-@item byte-code
-The string containing the byte-code instructions.
+@item code
+For interpreted functions, this element is the (non-empty) list of Lisp
+forms that make up the function's body.  For byte-compiled functions, it
+is the string containing the byte-code instructions.
 
 @item constants
-The vector of Lisp objects referenced by the byte code.  These include
-symbols used as function names and variable names.
+For byte-compiled functions, this holds the vector of Lisp objects
+referenced by the byte code.  These include symbols used as function
+names and variable names.
+For interpreted functions, this is nil if the function is using the old
+dynamically scoped dialect of Emacs Lisp, and otherwise it holds the
+function's lexical environment.
 
 @item stacksize
-The maximum stack size this function needs.
+The maximum stack size this function needs.  This element is left unused
+for interpreted functions.
 
 @item docstring
 The documentation string (if any); otherwise, @code{nil}.  The value may
@@ -558,8 +567,8 @@ Byte-Code Objects
 @code{make-byte-code}:
 
 @defun make-byte-code &rest elements
-This function constructs and returns a byte-code function object
-with @var{elements} as its elements.
+This function constructs and returns a closure which represents the
+byte-code function object with @var{elements} as its elements.
 @end defun
 
   You should not try to come up with the elements for a byte-code
@@ -567,6 +576,20 @@ Byte-Code Objects
 when you call the function.  Always leave it to the byte compiler to
 create these objects; it makes the elements consistent (we hope).
 
+The primitive way to create an interpreted function is with
+@code{make-interpreted-closure}:
+
+@defun make-interpreted-closure args body env &optional docstring iform
+This function constructs and returns a closure representing the
+interpreted function with arguments @var{args} and whose body is made of
+@var{body} which must be a non-nil list of Lisp forms.  @var{env} is the
+lexical environment in the same form as used with @code{eval}
+(@pxref{Eval}).  The documentation @var{docstring} if non-nil should be
+a string, and the interactive form @var{iform} if non-nil should be of
+the form @code{(interactive @var{arg-descriptor})} (@pxref{Using
+Interactive}).
+@end defun
+
 @node Disassembly
 @section Disassembled Byte-Code
 @cindex disassembled byte-code
@@ -595,7 +618,7 @@ Disassembly
 point is left before the output.
 
 The argument @var{object} can be a function name, a lambda expression
-(@pxref{Lambda Expressions}), or a byte-code object (@pxref{Byte-Code
+(@pxref{Lambda Expressions}), or a byte-code object (@pxref{Closure
 Objects}).  If it is a lambda expression, @code{disassemble} compiles
 it and disassembles the resulting compiled code.
 @end deffn
diff --git a/doc/lispref/elisp.texi b/doc/lispref/elisp.texi
index ec93a0b9c8a..339272d1f05 100644
--- a/doc/lispref/elisp.texi
+++ b/doc/lispref/elisp.texi
@@ -323,7 +323,7 @@ Top
 * Macro Type::          A method of expanding an expression into another
                           expression, more fundamental but less pretty.
 * Primitive Function Type::     A function written in C, callable from Lisp.
-* Byte-Code Type::      A function written in Lisp, then compiled.
+* Closure Type::        A function written in Lisp, then compiled.
 * Record Type::         Compound objects with programmer-defined types.
 * Type Descriptors::    Objects holding information about types.
 * Autoload Type::       A type used for automatically loading seldom-used
@@ -657,7 +657,7 @@ Top
 * Docs and Compilation::    Dynamic loading of documentation strings.
 * Eval During Compile::     Code to be evaluated when you compile.
 * Compiler Errors::         Handling compiler error messages.
-* Byte-Code Objects::       The data type used for byte-compiled functions.
+* Closure Objects::         The data type used for byte-compiled functions.
 * Disassembly::             Disassembling byte-code; how to read byte-code.
 
 Native Compilation
diff --git a/doc/lispref/functions.texi b/doc/lispref/functions.texi
index 0062de91354..c57de08460f 100644
--- a/doc/lispref/functions.texi
+++ b/doc/lispref/functions.texi
@@ -130,7 +130,7 @@ What Is a Function
 
 @item byte-code function
 A function that has been compiled by the byte compiler.
-@xref{Byte-Code Type}.
+@xref{Closure Type}.
 
 @item autoload object
 @cindex autoload object
@@ -227,6 +227,16 @@ What Is a Function
 a function loaded from a dynamic module (@pxref{Dynamic Modules}).
 @end defun
 
+@defun interpreted-function-p object
+This function returns @code{t} if @var{object} is an interpreted function.
+@end defun
+
+@defun closurep object
+This function returns @code{t} if @var{object} is a closure, which is
+a particular kind of function object.  Currently closures are used
+for all byte-code functions and all interpreted functions.
+@end defun
+
 @defun subr-arity subr
 This works like @code{func-arity}, but only for built-in functions and
 without symbol indirection.  It signals an error for non-built-in
diff --git a/doc/lispref/objects.texi b/doc/lispref/objects.texi
index aa1e073042f..cf703aba9c8 100644
--- a/doc/lispref/objects.texi
+++ b/doc/lispref/objects.texi
@@ -244,7 +244,7 @@ Programming Types
 * Macro Type::          A method of expanding an expression into another
                           expression, more fundamental but less pretty.
 * Primitive Function Type::     A function written in C, callable from Lisp.
-* Byte-Code Type::      A function written in Lisp, then compiled.
+* Closure Type::        A function written in Lisp.
 * Record Type::         Compound objects with programmer-defined types.
 * Type Descriptors::    Objects holding information about types.
 * Autoload Type::       A type used for automatically loading seldom-used
@@ -1458,18 +1458,24 @@ Primitive Function Type
 @end group
 @end example
 
-@node Byte-Code Type
-@subsection Byte-Code Function Type
+@node Closure Type
+@subsection Closure Function Type
 
-@dfn{Byte-code function objects} are produced by byte-compiling Lisp
-code (@pxref{Byte Compilation}).  Internally, a byte-code function
-object is much like a vector; however, the evaluator handles this data
-type specially when it appears in a function call.  @xref{Byte-Code
-Objects}.
+@dfn{Closures} are function objects produced when turning a function
+definition into a function value.  Closures are used both for
+byte-compiled Lisp functions as well as for interpreted Lisp functions.
+Closures can be produced by byte-compiling Lisp code (@pxref{Byte
+Compilation}) or simply by evaluating a lambda expression without
+compiling it, resulting in an interpreted function.  Internally,
+a closure is much like a vector; however, the evaluator
+handles this data type specially when it appears in a function call.
+@xref{Closure Objects}.
 
 The printed representation and read syntax for a byte-code function
 object is like that for a vector, with an additional @samp{#} before the
-opening @samp{[}.
+opening @samp{[}.  When printed for human consumption, it is printed as
+a special kind of list with an additional @samp{#f} before the opening
+@samp{(}.
 
 @node Record Type
 @subsection Record Type
@@ -2042,10 +2048,7 @@ Type Predicates
 @xref{Buffer Basics, bufferp}.
 
 @item byte-code-function-p
-@xref{Byte-Code Type, byte-code-function-p}.
-
-@item compiled-function-p
-@xref{Byte-Code Type, compiled-function-p}.
+@xref{Closure Type, byte-code-function-p}.
 
 @item case-table-p
 @xref{Case Tables, case-table-p}.
@@ -2056,9 +2059,15 @@ Type Predicates
 @item char-table-p
 @xref{Char-Tables, char-table-p}.
 
+@item closurep
+@xref{What Is a Function, closurep}.
+
 @item commandp
 @xref{Interactive Call, commandp}.
 
+@item compiled-function-p
+@xref{Closure Type, compiled-function-p}.
+
 @item condition-variable-p
 @xref{Condition Variables, condition-variable-p}.
 
@@ -2098,6 +2107,9 @@ Type Predicates
 @item integerp
 @xref{Predicates on Numbers, integerp}.
 
+@item interpreted-function-p
+@xref{What Is a Function, interpreted-function-p}.
+
 @item keymapp
 @xref{Creating Keymaps, keymapp}.
 
diff --git a/doc/lispref/sequences.texi b/doc/lispref/sequences.texi
index c9e47624878..4c5525f10c5 100644
--- a/doc/lispref/sequences.texi
+++ b/doc/lispref/sequences.texi
@@ -1583,7 +1583,7 @@ Vector Functions
 
 The @code{vconcat} function also allows byte-code function objects as
 arguments.  This is a special feature to make it easy to access the entire
-contents of a byte-code function object.  @xref{Byte-Code Objects}.
+contents of a byte-code function object.  @xref{Closure Objects}.
 
 For other concatenation functions, see @code{mapconcat} in @ref{Mapping
 Functions}, @code{concat} in @ref{Creating Strings}, and @code{append}
diff --git a/doc/lispref/text.texi b/doc/lispref/text.texi
index 0cd4e2c614e..07fb730f0f1 100644
--- a/doc/lispref/text.texi
+++ b/doc/lispref/text.texi
@@ -6386,8 +6386,12 @@ Change Hooks
 instead.
 @end defvar
 
+@menu
+* Tracking changes::       Keeping track of buffer modifications.
+@end menu
+
 @node Tracking changes
-@subsection Tracking changes
+@subsection Keeping track of buffer modifications
 @cindex track-changes
 @cindex change tracker
 
diff --git a/src/.gdbinit b/src/.gdbinit
index 6c4dda67f06..7645d466a5e 100644
--- a/src/.gdbinit
+++ b/src/.gdbinit
@@ -822,15 +822,22 @@ Print $ as a frame pointer.
 This command assumes $ is an Emacs Lisp frame value.
 end
 
-define xcompiled
+define xclosure
   xgetptr $
   print (struct Lisp_Vector *) $ptr
   output ($->contents[0])@($->header.size & 0xff)
   echo \n
 end
+document xclosure
+Print $ as a function pointer.
+This command assumes that $ is an Emacs Lisp byte-code or interpreted function value.
+end
+
+define xcompiled
+  xclosure
+end
 document xcompiled
-Print $ as a compiled function pointer.
-This command assumes that $ is an Emacs Lisp compiled value.
+Obsolete alias for "xclosure".
 end
 
 define xwindow
@@ -1038,8 +1045,8 @@ define xpr
       if $vec == PVEC_FRAME
 	xframe
       end
-      if $vec == PVEC_COMPILED
-	xcompiled
+      if $vec == PVEC_CLOSURE
+	xclosure
       end
       if $vec == PVEC_WINDOW
 	xwindow
diff --git a/src/eval.c b/src/eval.c
index 2dfa869b03b..aa8ac4f10af 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -521,6 +521,9 @@ DEFUN ("make-interpreted-closure", Fmake_interpreted_closure,
    Lisp_Object docstring, Lisp_Object iform)
 {
   CHECK_CONS (body);          /* Make sure it's not confused with byte-code! */
+  /* Despite its name 'Fmake_byte_code' works to build all kinds of 'closure'
+     objects, including interpreted functions, rather than only byte-code
+     functions.  */
   if (!NILP (iform))
     {
       iform = Fcdr (iform);

--=-=-=--





Information forwarded to bug-gnu-emacs@HIDDEN:
bug#70368; Package emacs. Full text available.

Message received at 70368 <at> debbugs.gnu.org:


Received: (at 70368) by debbugs.gnu.org; 14 Apr 2024 14:46:09 +0000
From debbugs-submit-bounces <at> debbugs.gnu.org Sun Apr 14 10:46:08 2024
Received: from localhost ([127.0.0.1]:35953 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1rw17J-0001ts-Ov
	for submit <at> debbugs.gnu.org; Sun, 14 Apr 2024 10:46:07 -0400
Received: from eggs.gnu.org ([2001:470:142:3::10]:47192)
 by debbugs.gnu.org with esmtp (Exim 4.84_2)
 (envelope-from <eliz@HIDDEN>) id 1rw171-0001rD-S0
 for 70368 <at> debbugs.gnu.org; Sun, 14 Apr 2024 10:45:56 -0400
Received: from fencepost.gnu.org ([2001:470:142:3::e])
 by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.90_1) (envelope-from <eliz@HIDDEN>)
 id 1rw16l-0008Bj-7Y; Sun, 14 Apr 2024 10:45:27 -0400
DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org;
 s=fencepost-gnu-org; h=References:Subject:In-Reply-To:To:From:Date:
 mime-version; bh=DI9pfoQFI6O410clNrhEwdcAIg8LI0d1fTF8tNrw3xI=; b=mcvVvQ57f+ds
 yRRNXjCI4fKrc2vZIE/cHdqrLe64tDgwRjSOOhoXTudOfFM74kNq7a4Ln08BuYqvZm22ScnzhePo/
 AxQJeVYkMmsaQuEcvP2xH3RtlGzS9B+k7V9u9hQjQkt+RRQY1D/YRbMHXX+Qo+9GRSJhfTzVodm4K
 C7EShy/7Yz1GN/h0RuXakTUuPVSqBljgLqgEOqWg1PxID8A6fhs9wmey6JWc5w8+YVQAuSkkMugKR
 bJMedhC+Uag/GO+sja0nzOqykULuglY/BzAa+g5m0F4txq20k9XdOLEDg2LpaBULktgZPSkf4SIP6
 D4Drp88i8WcY36Q9Y4deRg==;
Date: Sun, 14 Apr 2024 17:45:24 +0300
Message-Id: <86h6g4m29n.fsf@HIDDEN>
From: Eli Zaretskii <eliz@HIDDEN>
To: Stefan Monnier <monnier@HIDDEN>
In-Reply-To: <jwvo7acw226.fsf-monnier+emacs@HIDDEN> (message from Stefan
 Monnier on Sun, 14 Apr 2024 09:49:23 -0400)
Subject: Re: bug#70368: [PATCH] Use a dedicated type to represent
 interpreted-function values
References: <jwvo7adxcsp.fsf-monnier+@gnu.org> <86il0ko6f9.fsf@HIDDEN>
 <jwvo7acw226.fsf-monnier+emacs@HIDDEN>
X-Spam-Score: -2.3 (--)
X-Debbugs-Envelope-To: 70368
Cc: 70368 <at> debbugs.gnu.org
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -3.3 (---)

> From: Stefan Monnier <monnier@HIDDEN>
> Cc: 70368 <at> debbugs.gnu.org
> Date: Sun, 14 Apr 2024 09:49:23 -0400
> 
> > I don't think I understand the implications of this on compatibility
> > of byte-code.  Will the byte-code produced by Emacs 30 after these
> > changes be compatible or incompatible with previous versions of Emacs?
> 
> > And what about the compatibility in the other direction?
> 
> There are two kinds of incompatibilities it introduces, in my experience:
> 
> - "soft" incompatibilities for code which tries to display the kind of
>   the object it receives (such as in a completion table that wants to
>   add an icon indicating if something is a macro, a compiled function,
>   etc...).  Such code will still work but may display less informative
>   info because it may fail to recognize the new objects as being
>   interpreted functions.
> - "real" incompatibilities for code which digs inside the
>   entrails of functions to try and extract specific information.
>   This may fail when faced with the new interpreted functions
>   but should be easy to fix.
>   As long as such code only tries to extract info and does it via
>   `help-function-arglist`, `documentation`, and `interactive-form`,
>   there's no problem, but some packages may use code inherited from
>   a long time ago when `help-function-arglist` didn't exist, or written
>   by coders who didn't know better.  I know Hyperbole used to do that,
>   but I believe that's been fixed since.
> - "hard" incompatibilities for code which really digs inside the
>   code of functions.  `vc.el` did that a long time ago,
>   `kmacro.el` did as well until OClosures, and Buttercup (a NonGNU ELPA
>   package) did until a few months ago.  There are probably one or two
>   packages out there that will be affected like Buttercup would have
>   been.  FWIW, the Buttercup case was a mix of "hard" and "soft": it
>   really looked inside the code, but used that only to provide more
>   informative messages and had a fallback case when the code was
>   compiled, so it would still work OK.

Some of the above should be in NEWS, I think, in the "incompatible
Lisp changes" section.

> > This new #f syntax is not documented anywhere, AFAICT.  If this is the
> > new printed representation (and maybe also read syntax?) of functions,
> 
> It's only a cl-print representation, it's not `read`able.
> The `read`able representation uses the #[...] syntax also used for
> bytecode functions.
> 
> > it should be documented, like we do with other printed representations.
> 
> Where would that be?  I don't see where we document the
> #f(compiled-function ...) used for byte-code, which was my inspiration
> for the #f(lambda ...).

We have the "Printed Representation" node, and then the representation
of each type is documented where the type is documented, in
subsections of "Programming Types".  So somewhere there, I think,
probably in "Function Type"?  And you probbaly want to review
"Byte-Code Type" as well.

> >> @@ -5571,7 +5575,7 @@ display-call-tree
> >>  		     " <compiled macro>"
> >>  		   " <macro>"))
> >>  		((eq 'lambda (car f))
> >> -		 "<function>")
> >> +		 "<function-like list>")
> >                   ^^^^^^^^^^^^^^^^^^^^
> > Should this be documented somewhere?
> 
> This should be an extremely rare occurrence, and `display-call-tree`
> itself is not documented, so I doubt it's worth the trouble.
> If you prefer I can keep it as `<function>`.

I thing <function> is preferable, since <function-like list> is
somewhat mysterious, and what you say above means explaining the fine
differences will be largely a wasted effort.

> >> +DEFUN ("closurep", Fclosurep, Sclosurep,
> >> +       1, 1, 0,
> >> +       doc: /* Return t if OBJECT is a function object.  */)
> >
> > If the doc string is correct, then why is the function called
> > 'closurep'?  It's against mnemonic memory.
> 
> The term "closure" is kind of funny, indeed: often it's used to say
> "this is a piece of code packaged with its environment", but in most
> cases where it's used all functions are like that, so the precise
> meaning of "closure" can be argued to be exactly the same as "function",
> just with a different connotation.  Sometimes the connotation is to
> insist on the fact that it captured some variables, but object it's used
> to distinguish between the abstract notion of functions and their
> runtime representation, where "closure" means "an object which
> represents a function".
> 
> IOW, the docstring above is meant to say "a function *object*" rather
> than "a *function* object".  Compare with `functionp`:
> 
>     Return t if OBJECT is a function.
> 
> Maybe I should say something like:
> 
>     Return t if OBJECT is a closure, i.e. a function object.
> 
> ?

Yes, I think that'd be better.

> >> +DEFUN ("interpreted-function-p", Finterpreted_function_p,
> >> +       Sinterpreted_function_p, 1, 1, 0,
> >> +       doc: /* Return t if OBJECT is an interpreted function value.  */)
> >                                                        ^^^^^^^^^^^^^^
> > "function value"? what's that?
> 
> In general, `eval` takes an source expression and returns a value.
> 
>     (lambda (x) (+ x y))
> 
> is a list of length 3 which represents a function as a source expression
> and `eval` turns it into a value (presumably a closure which will
> remember the current binding of `y`).
> 
>     (closure .. (x) (+ x y))
> 
> used to be the representation used for interpreted function *values*.

The problem is that "function value" can be interpreted as "value
returned by a function".  So I suggest

  Return t if OBJECT is a value that represents an interpreted function.

> >> +      return CALLN (Fmake_byte_code,
> >> +                    args, body, env, Qnil, docstring,
> >> +                    NILP (Fcdr (iform))
> >> +                    ? Fcar (iform)
> >> +                    : CALLN (Fvector, XCAR (iform), XCDR (iform)));
> >> +    }
> >> +  else if (!NILP (docstring))
> >> +    return CALLN (Fmake_byte_code, args, body, env, Qnil, docstring);
> >> +  else
> >> +    return CALLN (Fmake_byte_code, args, body, env);
> >
> > I'm probably missing something, but if the doc string says "make an
> > _interpreted_ closure", why does the implementation call
> > make-byte-code?  Isn't byte-code a kind-of antithesis of
> > "interpreted"?
> 
> Because `make-byte-code` is the function that we already have which
> creates PVEC_CLOSURE (previous called PVEC_COMPILED) objects.  It used
> to be used exclusively to create byte-code functions but now that same
> representation is used for interpreted function.  I could rename it but
> we already have `make-closure` for something slightly different, and
> I don't see much benefit to the rename.
> I'll add a comment explaining why we use `Fmake_byte_code`,

I think this also calls for augmenting the documentation of
make-byte-code to the effect that it could now create closures as
well.




Information forwarded to bug-gnu-emacs@HIDDEN:
bug#70368; Package emacs. Full text available.

Message received at 70368 <at> debbugs.gnu.org:


Received: (at 70368) by debbugs.gnu.org; 14 Apr 2024 13:49:54 +0000
From debbugs-submit-bounces <at> debbugs.gnu.org Sun Apr 14 09:49:54 2024
Received: from localhost ([127.0.0.1]:34505 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1rw0Ew-00074G-1P
	for submit <at> debbugs.gnu.org; Sun, 14 Apr 2024 09:49:53 -0400
Received: from mailscanner.iro.umontreal.ca ([132.204.25.50]:2936)
 by debbugs.gnu.org with esmtp (Exim 4.84_2)
 (envelope-from <monnier@HIDDEN>) id 1rw0Eq-00072V-3O
 for 70368 <at> debbugs.gnu.org; Sun, 14 Apr 2024 09:49:47 -0400
Received: from pmg3.iro.umontreal.ca (localhost [127.0.0.1])
 by pmg3.iro.umontreal.ca (Proxmox) with ESMTP id 0E8B0441302;
 Sun, 14 Apr 2024 09:49:27 -0400 (EDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=iro.umontreal.ca;
 s=mail; t=1713102564;
 bh=s0G5Y0ibm8hpLNG3WfETz7uU7s5qCZEgcG0CUadI6No=;
 h=From:To:Cc:Subject:In-Reply-To:References:Date:From;
 b=hqXh9QI+EtRglpmLf6WK5VKA3vpiDPGWHhrYkPEBNUy/eHRX8genK2TT/+mghiksM
 JI7+62a2t8czeXUnfOM1azHZ/3+KDikP26+WRX7K4LvOz7C0NmUtAB/0tQDnO/jBd6
 h1sQLug/QfXhzD0y36ZlZpnELPXjmDOssQWqYp0ONR7F5zRES/6832snSGM2OiV2pB
 IeI27bpDGiCRT/ONsanNP9CjxAhW1ed/bVFnyUMbqAz4iRA5Kjs5ARfAidRsyPHZ1A
 qXYSkPKxe04OMfA19DGaEkwtPC3pM/w2NZQrWFfgB1PZZwb0/asWD6iA2MdERcaF23
 9PdSaLuL8IqzQ==
Received: from mail01.iro.umontreal.ca (unknown [172.31.2.1])
 by pmg3.iro.umontreal.ca (Proxmox) with ESMTP id 52A7A4412EB;
 Sun, 14 Apr 2024 09:49:24 -0400 (EDT)
Received: from pastel (unknown [45.72.201.215])
 by mail01.iro.umontreal.ca (Postfix) with ESMTPSA id 309CB120185;
 Sun, 14 Apr 2024 09:49:24 -0400 (EDT)
From: Stefan Monnier <monnier@HIDDEN>
To: Eli Zaretskii <eliz@HIDDEN>
Subject: Re: bug#70368: [PATCH] Use a dedicated type to represent
 interpreted-function values
In-Reply-To: <86il0ko6f9.fsf@HIDDEN> (Eli Zaretskii's message of "Sun, 14 Apr
 2024 08:32:42 +0300")
Message-ID: <jwvo7acw226.fsf-monnier+emacs@HIDDEN>
References: <jwvo7adxcsp.fsf-monnier+@gnu.org> <86il0ko6f9.fsf@HIDDEN>
Date: Sun, 14 Apr 2024 09:49:23 -0400
User-Agent: Gnus/5.13 (Gnus v5.13)
MIME-Version: 1.0
Content-Type: text/plain
X-SPAM-INFO: Spam detection results:  0
 ALL_TRUSTED                -1 Passed through trusted hosts only via SMTP
 AWL 0.002 Adjusted score from AWL reputation of From: address
 BAYES_00                 -1.9 Bayes spam probability is 0 to 1%
 DKIM_SIGNED               0.1 Message has a DKIM or DK signature,
 not necessarily valid
 DKIM_VALID -0.1 Message has at least one valid DKIM or DK signature
 DKIM_VALID_AU -0.1 Message has a valid DKIM or DK signature from author's
 domain
 DKIM_VALID_EF -0.1 Message has a valid DKIM or DK signature from envelope-from
 domain
X-SPAM-LEVEL: 
X-Spam-Score: -2.3 (--)
X-Debbugs-Envelope-To: 70368
Cc: 70368 <at> debbugs.gnu.org
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -3.3 (---)

> I don't think I understand the implications of this on compatibility
> of byte-code.  Will the byte-code produced by Emacs 30 after these
> changes be compatible or incompatible with previous versions of Emacs?

The byte code is unchanged, so compatible both ways.

Some new `.elc` files may be incompatible with older Emacsen if they
happen to contain a non-compiled function (e.g. via a macro which
somehow gets an interpreted function during the expansion (e.g. via
`eval` of by fetching it from a not-yet-compiled file) and then stashes
it in the returned code).  FWIW, I don't see any occurrence of that in
Emacs's own files, for example.

> And what about the compatibility in the other direction?

There are two kinds of incompatibilities it introduces, in my experience:

- "soft" incompatibilities for code which tries to display the kind of
  the object it receives (such as in a completion table that wants to
  add an icon indicating if something is a macro, a compiled function,
  etc...).  Such code will still work but may display less informative
  info because it may fail to recognize the new objects as being
  interpreted functions.
- "real" incompatibilities for code which digs inside the
  entrails of functions to try and extract specific information.
  This may fail when faced with the new interpreted functions
  but should be easy to fix.
  As long as such code only tries to extract info and does it via
  `help-function-arglist`, `documentation`, and `interactive-form`,
  there's no problem, but some packages may use code inherited from
  a long time ago when `help-function-arglist` didn't exist, or written
  by coders who didn't know better.  I know Hyperbole used to do that,
  but I believe that's been fixed since.
- "hard" incompatibilities for code which really digs inside the
  code of functions.  `vc.el` did that a long time ago,
  `kmacro.el` did as well until OClosures, and Buttercup (a NonGNU ELPA
  package) did until a few months ago.  There are probably one or two
  packages out there that will be affected like Buttercup would have
  been.  FWIW, the Buttercup case was a mix of "hard" and "soft": it
  really looked inside the code, but used that only to provide more
  informative messages and had a fallback case when the code was
  compiled, so it would still work OK.
  
>> The first point above means that `help-function-arglist`,
>> `documentation`, and `interactive-form`s don't need to
>> distinguish interpreted and bytecode functions any more.
> Why is such a distinction needed?

Indeed, why?
[ More seriously, what I'm saying above is that for those three
  functions, the code used for byte-code objects now also works on
  interpreted functions by virtue of both objects sharing the same
  underlying representation.  ]

>> Main benefits of the change:
>> 
>> - We can now reliably distinguish a list from a function value.
>>   This removes some ambiguity in cases where the data can be made of
>>   a list or a function, such as `run-hooks` or completion tables.
>> - `cl-defmethod` can dispatch on `interactive-function`.
>>   Dispatch on `function` also works now for interpreted functions (but still
>>   won't work for functions represented as lists or as symbols, of course).
>> - Function values are now self-evaluating.  That was already the case
>>   when byte-compiled, but not when interpreted since
>>   (eval '(closure ...)) signals a void-function error.
>>   That also avoids false-positive warnings about "don't quote your lambdas"
>>   when doing things like `(mapcar ',func ...)`.
>
> Are there no downsides, none whatever?  It would sound too good to be
> true...

There's the backward incompatibilty.

> Do the above changes require changes to .gdbinit?  E.g., I see that we
> use PVEC_COMPILED there.

Indeed, I missed that, thanks, fixed.

> This new #f syntax is not documented anywhere, AFAICT.  If this is the
> new printed representation (and maybe also read syntax?) of functions,

It's only a cl-print representation, it's not `read`able.
The `read`able representation uses the #[...] syntax also used for
bytecode functions.

> it should be documented, like we do with other printed representations.

Where would that be?  I don't see where we document the
#f(compiled-function ...) used for byte-code, which was my inspiration
for the #f(lambda ...).

>> --- a/lisp/emacs-lisp/byte-opt.el
>> +++ b/lisp/emacs-lisp/byte-opt.el
>> @@ -164,7 +164,7 @@ byte-compile-inline-expand
>>         ;; The byte-code will be really inlined in byte-compile-unfold-bcf.
>>         (byte-compile--check-arity-bytecode form fn)
>>         `(,fn ,@(cdr form)))
>> -      ((or `(lambda . ,_) `(closure . ,_))
>> +      ((pred interpreted-function-p)
>
> What does this change mean for backward compatibility?

Nothing, it just reflects the fact that this code previously received
interpreted function in the form of (closure ...) and now it receives
them in the form of `interpreted-function` objects.

>> diff --git a/lisp/emacs-lisp/bytecomp.el b/lisp/emacs-lisp/bytecomp.el
>> index fb3278c08ab..c82f8ba6ae2 100644
>> --- a/lisp/emacs-lisp/bytecomp.el
>> +++ b/lisp/emacs-lisp/bytecomp.el
>> @@ -2900,9 +2900,14 @@ byte-compile-output-as-comment
>>  (defun byte-compile--reify-function (fun)
>>    "Return an expression which will evaluate to a function value FUN.
>>  FUN should be an interpreted closure."
>> -  (pcase-let* ((`(closure ,env ,args . ,body) fun)
>> -               (`(,preamble . ,body) (macroexp-parse-body body))
>> -               (renv ()))
>> +  (let* ((args (aref fun 0))
>> +         (body (aref fun 1))
>> +         (env (aref fun 2))
>> +         (docstring (function-documentation fun))
>> +         (iform (interactive-form fun))
>> +         (preamble `(,@(if docstring (list docstring))
>> +                     ,@(if iform (list iform))))
>> +         (renv ()))
>
> And this?

Same: this adjust the internal code of `byte-compile` so it works on the
new representation of function values.

>> @@ -5571,7 +5575,7 @@ display-call-tree
>>  		     " <compiled macro>"
>>  		   " <macro>"))
>>  		((eq 'lambda (car f))
>> -		 "<function>")
>> +		 "<function-like list>")
>                   ^^^^^^^^^^^^^^^^^^^^
> Should this be documented somewhere?

This should be an extremely rare occurrence, and `display-call-tree`
itself is not documented, so I doubt it's worth the trouble.
If you prefer I can keep it as `<function>`.

>> +DEFUN ("closurep", Fclosurep, Sclosurep,
>> +       1, 1, 0,
>> +       doc: /* Return t if OBJECT is a function object.  */)
>
> If the doc string is correct, then why is the function called
> 'closurep'?  It's against mnemonic memory.

The term "closure" is kind of funny, indeed: often it's used to say
"this is a piece of code packaged with its environment", but in most
cases where it's used all functions are like that, so the precise
meaning of "closure" can be argued to be exactly the same as "function",
just with a different connotation.  Sometimes the connotation is to
insist on the fact that it captured some variables, but object it's used
to distinguish between the abstract notion of functions and their
runtime representation, where "closure" means "an object which
represents a function".

IOW, the docstring above is meant to say "a function *object*" rather
than "a *function* object".  Compare with `functionp`:

    Return t if OBJECT is a function.

Maybe I should say something like:

    Return t if OBJECT is a closure, i.e. a function object.

?

>> +DEFUN ("interpreted-function-p", Finterpreted_function_p,
>> +       Sinterpreted_function_p, 1, 1, 0,
>> +       doc: /* Return t if OBJECT is an interpreted function value.  */)
>                                                        ^^^^^^^^^^^^^^
> "function value"? what's that?

In general, `eval` takes an source expression and returns a value.

    (lambda (x) (+ x y))

is a list of length 3 which represents a function as a source expression
and `eval` turns it into a value (presumably a closure which will
remember the current binding of `y`).

    (closure .. (x) (+ x y))

used to be the representation used for interpreted function *values*.

>> +  (Lisp_Object object)
>> +{
>> +  if (CLOSUREP (object) && CONSP (AREF (object, CLOSURE_CODE)))
>>      return Qt;
>>    return Qnil;
>
> These new primitives should be documented in the ELisp manual, and
> perhaps also mentioned in NEWS.

OK.

>> +DEFUN ("make-interpreted-closure", Fmake_interpreted_closure,
>> +       Smake_interpreted_closure, 3, 5, 0,
>> +       doc: /* Make an interpreted closure.
>
> Please try to mention the arguments in the first sentence of doc
> string.
>
> And this primitive should also be documented in the ELisp manual.

OK.

>> +ARGS should be the list of formal arguments.
>> +BODY should be a non-empty list of forms.
>> +ENV should be a lexical environment, like the second argument of `eval'.
>> +IFORM if non-nil should be of the form (interactive ...).  */)
>> +  (Lisp_Object args, Lisp_Object body, Lisp_Object env,
>> +   Lisp_Object docstring, Lisp_Object iform)
>> +{
>> +  CHECK_CONS (body);          /* Make sure it's not confused with byte-code! */
>> +  if (!NILP (iform))
>> +    {
>> +      iform = Fcdr (iform);
>> +      return CALLN (Fmake_byte_code,
>> +                    args, body, env, Qnil, docstring,
>> +                    NILP (Fcdr (iform))
>> +                    ? Fcar (iform)
>> +                    : CALLN (Fvector, XCAR (iform), XCDR (iform)));
>> +    }
>> +  else if (!NILP (docstring))
>> +    return CALLN (Fmake_byte_code, args, body, env, Qnil, docstring);
>> +  else
>> +    return CALLN (Fmake_byte_code, args, body, env);
>
> I'm probably missing something, but if the doc string says "make an
> _interpreted_ closure", why does the implementation call
> make-byte-code?  Isn't byte-code a kind-of antithesis of
> "interpreted"?

Because `make-byte-code` is the function that we already have which
creates PVEC_CLOSURE (previous called PVEC_COMPILED) objects.  It used
to be used exclusively to create byte-code functions but now that same
representation is used for interpreted function.  I could rename it but
we already have `make-closure` for something slightly different, and
I don't see much benefit to the rename.
I'll add a comment explaining why we use `Fmake_byte_code`,


        Stefan





Information forwarded to bug-gnu-emacs@HIDDEN:
bug#70368; Package emacs. Full text available.

Message received at 70368 <at> debbugs.gnu.org:


Received: (at 70368) by debbugs.gnu.org; 14 Apr 2024 05:33:09 +0000
From debbugs-submit-bounces <at> debbugs.gnu.org Sun Apr 14 01:33:09 2024
Received: from localhost ([127.0.0.1]:34098 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1rvsUG-00046r-2k
	for submit <at> debbugs.gnu.org; Sun, 14 Apr 2024 01:33:09 -0400
Received: from eggs.gnu.org ([2001:470:142:3::10]:39790)
 by debbugs.gnu.org with esmtp (Exim 4.84_2)
 (envelope-from <eliz@HIDDEN>) id 1rvsUA-00044j-Ku
 for 70368 <at> debbugs.gnu.org; Sun, 14 Apr 2024 01:33:06 -0400
Received: from fencepost.gnu.org ([2001:470:142:3::e])
 by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.90_1) (envelope-from <eliz@HIDDEN>)
 id 1rvsTt-00070s-LO; Sun, 14 Apr 2024 01:32:45 -0400
DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org;
 s=fencepost-gnu-org; h=References:Subject:In-Reply-To:To:From:Date:
 mime-version; bh=rRNYb7Pym4DGAhcWrLuAschL5yZdpR/zPiKqjzyJ3vA=; b=NJcH3h3IGi80
 HGJIL++hVM5MmnhryJa6p3rtHZSMQADU8cfyAGUITsyCL9FOTF6J3lE0gdMQdVcS8BCWLJUlO8Lxg
 7CT9fMP2DDY/Qg06VVuSYP8lZ3u6zKTVDMXUlhXUtTkDSjyI7Vz3zjocyagicbA6iqDF5W+mrG+P5
 N5QSo/sYTZJf0XVmJ7A/LP4CZOdCWTwRJK/YwMLkStRQSPEpnShUd03vvN82gZ8opwzd6ubdQLchv
 ObjH08CPbcwOV67xCYioTx8peS9PFaOPjxCgzjuk0Tc6OpmWN33R5VMccB6ZBIe84K8g9EdHr6XLU
 IcUt5QVzAhtcD+uexSnQ+Q==;
Date: Sun, 14 Apr 2024 08:32:42 +0300
Message-Id: <86il0ko6f9.fsf@HIDDEN>
From: Eli Zaretskii <eliz@HIDDEN>
To: Stefan Monnier <monnier@HIDDEN>
In-Reply-To: <jwvo7adxcsp.fsf-monnier+@gnu.org> (bug-gnu-emacs@HIDDEN)
Subject: Re: bug#70368: [PATCH] Use a dedicated type to represent
 interpreted-function values
References: <jwvo7adxcsp.fsf-monnier+@gnu.org>
X-Spam-Score: -2.3 (--)
X-Debbugs-Envelope-To: 70368
Cc: 70368 <at> debbugs.gnu.org
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -3.3 (---)

> Date: Sat, 13 Apr 2024 15:56:34 -0400
> From:  Stefan Monnier via "Bug reports for GNU Emacs,
>  the Swiss army knife of text editors" <bug-gnu-emacs@HIDDEN>
> 
> Change `function` so that when evaluating #'(lambda ...)
> we return an object of type `interpreted-function` rather than
> a list starting with one of `lambda` or `closure`.
> The new type reuses the existing PVEC_CLOSURE (nee PVEC_COMPILED)
> tag used for byte-code function and tries to align the corresponding
> elements:
> 
> - the arglist, the docstring, and the interactive-form go in the
>   same slots as for byte-code functions.
> - the body of the function goes in the slot used for the bytecode string.
> - the lexical context goes in the slot used for the constants of
>   bytecoded functions.

I don't think I understand the implications of this on compatibility
of byte-code.  Will the byte-code produced by Emacs 30 after these
changes be compatible or incompatible with previous versions of Emacs?
And what about the compatibility in the other direction?

> The first point above means that `help-function-arglist`,
> `documentation`, and `interactive-form`s don't need to
> distinguish interpreted and bytecode functions any more.

Why is such a distinction needed?

> Main benefits of the change:
> 
> - We can now reliably distinguish a list from a function value.
>   This removes some ambiguity in cases where the data can be made of
>   a list or a function, such as `run-hooks` or completion tables.
> - `cl-defmethod` can dispatch on `interactive-function`.
>   Dispatch on `function` also works now for interpreted functions (but still
>   won't work for functions represented as lists or as symbols, of course).
> - Function values are now self-evaluating.  That was already the case
>   when byte-compiled, but not when interpreted since
>   (eval '(closure ...)) signals a void-function error.
>   That also avoids false-positive warnings about "don't quote your lambdas"
>   when doing things like `(mapcar ',func ...)`.

Are there no downsides, none whatever?  It would sound too good to be
true...

> -enum Lisp_Compiled
> +enum Lisp_Closure
>    {
> -    COMPILED_ARGLIST = 0,
> -    COMPILED_BYTECODE = 1,
> -    COMPILED_CONSTANTS = 2,
> -    COMPILED_STACK_DEPTH = 3,
> -    COMPILED_DOC_STRING = 4,
> -    COMPILED_INTERACTIVE = 5
> +    CLOSURE_ARGLIST = 0,
> +    CLOSURE_CODE = 1,
> +    CLOSURE_CONSTANTS = 2,
> +    CLOSURE_STACK_DEPTH = 3,
> +    CLOSURE_DOC_STRING = 4,
> +    CLOSURE_INTERACTIVE = 5
>    };
>  
>  /* Flag bits in a character.  These also get used in termhooks.h.
> @@ -3307,9 +3307,9 @@ WINDOW_CONFIGURATIONP (Lisp_Object a)
>  }
>  
>  INLINE bool
> -COMPILEDP (Lisp_Object a)
> +CLOSUREP (Lisp_Object a)
>  {
> -  return PSEUDOVECTORP (a, PVEC_COMPILED);
> +  return PSEUDOVECTORP (a, PVEC_CLOSURE);
>  }

Do the above changes require changes to .gdbinit?  E.g., I see that we
use PVEC_COMPILED there.

> --- a/doc/lispref/control.texi
> +++ b/doc/lispref/control.texi
> @@ -2411,7 +2411,7 @@ Handling Errors
>  @group
>  Debugger entered--Lisp error: (error "Oops")
>    signal(error ("Oops"))
> -  (closure (t) (err) (signal 'error (cdr err)))((user-error "Oops"))
> +  #f(lambda (err) [t] (signal 'error (cdr err)))((user-error "Oops"))
>    user-error("Oops")

This new #f syntax is not documented anywhere, AFAICT.  If this is the
new printed representation (and maybe also read syntax?) of functions,
it should be documented, like we do with other printed representations.

> --- a/lisp/emacs-lisp/byte-opt.el
> +++ b/lisp/emacs-lisp/byte-opt.el
> @@ -164,7 +164,7 @@ byte-compile-inline-expand
>         ;; The byte-code will be really inlined in byte-compile-unfold-bcf.
>         (byte-compile--check-arity-bytecode form fn)
>         `(,fn ,@(cdr form)))
> -      ((or `(lambda . ,_) `(closure . ,_))
> +      ((pred interpreted-function-p)

What does this change mean for backward compatibility?

> diff --git a/lisp/emacs-lisp/bytecomp.el b/lisp/emacs-lisp/bytecomp.el
> index fb3278c08ab..c82f8ba6ae2 100644
> --- a/lisp/emacs-lisp/bytecomp.el
> +++ b/lisp/emacs-lisp/bytecomp.el
> @@ -2900,9 +2900,14 @@ byte-compile-output-as-comment
>  (defun byte-compile--reify-function (fun)
>    "Return an expression which will evaluate to a function value FUN.
>  FUN should be an interpreted closure."
> -  (pcase-let* ((`(closure ,env ,args . ,body) fun)
> -               (`(,preamble . ,body) (macroexp-parse-body body))
> -               (renv ()))
> +  (let* ((args (aref fun 0))
> +         (body (aref fun 1))
> +         (env (aref fun 2))
> +         (docstring (function-documentation fun))
> +         (iform (interactive-form fun))
> +         (preamble `(,@(if docstring (list docstring))
> +                     ,@(if iform (list iform))))
> +         (renv ()))

And this?

> @@ -5571,7 +5575,7 @@ display-call-tree
>  		     " <compiled macro>"
>  		   " <macro>"))
>  		((eq 'lambda (car f))
> -		 "<function>")
> +		 "<function-like list>")
                  ^^^^^^^^^^^^^^^^^^^^
Should this be documented somewhere?

> +DEFUN ("closurep", Fclosurep, Sclosurep,
> +       1, 1, 0,
> +       doc: /* Return t if OBJECT is a function object.  */)

If the doc string is correct, then why is the function called
'closurep'?  It's against mnemonic memory.

> +  (Lisp_Object object)
> +{
> +  if (CLOSUREP (object))
> +    return Qt;
> +  return Qnil;
> +}
> +
>  DEFUN ("byte-code-function-p", Fbyte_code_function_p, Sbyte_code_function_p,
>         1, 1, 0,
>         doc: /* Return t if OBJECT is a byte-compiled function object.  */)
>    (Lisp_Object object)
>  {
> -  if (CLOSUREP (object))
> +  if (CLOSUREP (object) && STRINGP (AREF (object, CLOSURE_CODE)))
> +    return Qt;
> +  return Qnil;
> +}
> +
> +DEFUN ("interpreted-function-p", Finterpreted_function_p,
> +       Sinterpreted_function_p, 1, 1, 0,
> +       doc: /* Return t if OBJECT is an interpreted function value.  */)
                                                       ^^^^^^^^^^^^^^
"function value"? what's that?

> +  (Lisp_Object object)
> +{
> +  if (CLOSUREP (object) && CONSP (AREF (object, CLOSURE_CODE)))
>      return Qt;
>    return Qnil;

These new primitives should be documented in the ELisp manual, and
perhaps also mentioned in NEWS.

> +DEFUN ("make-interpreted-closure", Fmake_interpreted_closure,
> +       Smake_interpreted_closure, 3, 5, 0,
> +       doc: /* Make an interpreted closure.

Please try to mention the arguments in the first sentence of doc
string.

And this primitive should also be documented in the ELisp manual.

> +ARGS should be the list of formal arguments.
> +BODY should be a non-empty list of forms.
> +ENV should be a lexical environment, like the second argument of `eval'.
> +IFORM if non-nil should be of the form (interactive ...).  */)
> +  (Lisp_Object args, Lisp_Object body, Lisp_Object env,
> +   Lisp_Object docstring, Lisp_Object iform)
> +{
> +  CHECK_CONS (body);          /* Make sure it's not confused with byte-code! */
> +  if (!NILP (iform))
> +    {
> +      iform = Fcdr (iform);
> +      return CALLN (Fmake_byte_code,
> +                    args, body, env, Qnil, docstring,
> +                    NILP (Fcdr (iform))
> +                    ? Fcar (iform)
> +                    : CALLN (Fvector, XCAR (iform), XCDR (iform)));
> +    }
> +  else if (!NILP (docstring))
> +    return CALLN (Fmake_byte_code, args, body, env, Qnil, docstring);
> +  else
> +    return CALLN (Fmake_byte_code, args, body, env);

I'm probably missing something, but if the doc string says "make an
_interpreted_ closure", why does the implementation call
make-byte-code?  Isn't byte-code a kind-of antithesis of
"interpreted"?

Finally, please quote 'like this' or `like this' in the commit log
messages, not `like this`.  Example:

> In preparation for the use of `PVEC_COMPILED` objects for
> interpreted functions, rename them to use a more neutral name.
> 
> * src/lisp.h (enum pvec_type): Rename `PVEC_COMPILED` to `PVEC_CLOSURE`.
> (enum Lisp_Compiled): Use `CLOSURE_` prefix i.s.o `COMPILED_`.
> Also use `CODE` rather than `BYTECODE`.
> (CLOSUREP): Rename from `COMPILEDP`.
> (enum Lisp_Closure): Rename from `Lisp_Compiled`.

The line that begins with "* src/lisp.h" is too long, btw, it should
be at most 70 columns, preferably 65.  And there are a few others that
are likewise too long.

Thanks.




Information forwarded to bug-gnu-emacs@HIDDEN:
bug#70368; Package emacs. Full text available.

Message received at submit <at> debbugs.gnu.org:


Received: (at submit) by debbugs.gnu.org; 13 Apr 2024 19:57:23 +0000
From debbugs-submit-bounces <at> debbugs.gnu.org Sat Apr 13 15:57:23 2024
Received: from localhost ([127.0.0.1]:33835 helo=debbugs.gnu.org)
	by debbugs.gnu.org with esmtp (Exim 4.84_2)
	(envelope-from <debbugs-submit-bounces <at> debbugs.gnu.org>)
	id 1rvjV1-0002kc-Cg
	for submit <at> debbugs.gnu.org; Sat, 13 Apr 2024 15:57:23 -0400
Received: from lists.gnu.org ([2001:470:142::17]:60946)
 by debbugs.gnu.org with esmtp (Exim 4.84_2)
 (envelope-from <monnier@HIDDEN>) id 1rvjUp-0002ha-TR
 for submit <at> debbugs.gnu.org; Sat, 13 Apr 2024 15:57:18 -0400
Received: from eggs.gnu.org ([2001:470:142:3::10])
 by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.90_1) (envelope-from <monnier@HIDDEN>)
 id 1rvjUZ-0007cs-TE
 for bug-gnu-emacs@HIDDEN; Sat, 13 Apr 2024 15:56:51 -0400
Received: from mailscanner.iro.umontreal.ca ([132.204.25.50])
 by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
 (Exim 4.90_1) (envelope-from <monnier@HIDDEN>)
 id 1rvjUS-0004Ay-HP
 for bug-gnu-emacs@HIDDEN; Sat, 13 Apr 2024 15:56:51 -0400
Received: from pmg2.iro.umontreal.ca (localhost.localdomain [127.0.0.1])
 by pmg2.iro.umontreal.ca (Proxmox) with ESMTP id 87CD180C8F
 for <bug-gnu-emacs@HIDDEN>; Sat, 13 Apr 2024 15:56:42 -0400 (EDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=iro.umontreal.ca;
 s=mail; t=1713038196;
 bh=AhmD3cGtC435+Qk7mmAiSgFe+grFHbvYGJnnNqYf8X8=;
 h=From:To:Subject:Date:From;
 b=AOpTA3wCzlmEV66UOzmy9JvOxSJVczDi9CQ7//VKNuKQtxJq9fHr1h+alH/tP9GPL
 7DG40FWgd60UiiFXCDSaYpQE0IVIuy1QCT1sfbPa63zEvJBZyiAlqOh31ClOp2NFHL
 /xZmKUdINes/9utWdLT9n9aNb06haC64EK1snrTfzcoyTQjdRrGTV31mlaJTXd7Kl8
 +810cHMhQi6av7jlw7+2Q4Es4KjCKqz89FNLuYEVnFsLdSmseNGKAYLr49b8x5R/wU
 vZcSz9OZQSmhwJE06uZGwA0j/jfiP9pmWxn38w/ohjCDdGBfinMc8kaKuJ+lZo9zNh
 +YAoG3ZtV7rkA==
Received: from mail01.iro.umontreal.ca (unknown [172.31.2.1])
 by pmg2.iro.umontreal.ca (Proxmox) with ESMTP id 04391807A5
 for <bug-gnu-emacs@HIDDEN>; Sat, 13 Apr 2024 15:56:36 -0400 (EDT)
Received: from pastel (unknown [45.72.201.215])
 by mail01.iro.umontreal.ca (Postfix) with ESMTPSA id A4A6E12026E
 for <bug-gnu-emacs@HIDDEN>; Sat, 13 Apr 2024 15:56:35 -0400 (EDT)
From: Stefan Monnier <monnier@HIDDEN>
To: bug-gnu-emacs@HIDDEN
Subject: [PATCH] Use a dedicated type to represent interpreted-function values
Message-ID: <jwvo7adxcsp.fsf-monnier+@gnu.org>
X-Deddbugs-Cc: monnier@HIDDEN
Date: Sat, 13 Apr 2024 15:56:34 -0400
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="=-=-="
X-SPAM-INFO: Spam detection results:  0
 ALL_TRUSTED                -1 Passed through trusted hosts only via SMTP
 AWL -0.069 Adjusted score from AWL reputation of From: address
 BAYES_00                 -1.9 Bayes spam probability is 0 to 1%
 DKIM_SIGNED               0.1 Message has a DKIM or DK signature,
 not necessarily valid
 DKIM_VALID -0.1 Message has at least one valid DKIM or DK signature
 DKIM_VALID_AU -0.1 Message has a valid DKIM or DK signature from author's
 domain
 DKIM_VALID_EF -0.1 Message has a valid DKIM or DK signature from envelope-from
 domain
X-SPAM-LEVEL: 
Received-SPF: pass client-ip=132.204.25.50;
 envelope-from=monnier@HIDDEN; helo=mailscanner.iro.umontreal.ca
X-Spam_score_int: -42
X-Spam_score: -4.3
X-Spam_bar: ----
X-Spam_report: (-4.3 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1,
 DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, RCVD_IN_DNSWL_MED=-2.3,
 SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no
X-Spam_action: no action
X-Spam-Score: 0.0 (/)
X-Debbugs-Envelope-To: submit
X-BeenThere: debbugs-submit <at> debbugs.gnu.org
X-Mailman-Version: 2.1.18
Precedence: list
List-Id: <debbugs-submit.debbugs.gnu.org>
List-Unsubscribe: <https://debbugs.gnu.org/cgi-bin/mailman/options/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=unsubscribe>
List-Archive: <https://debbugs.gnu.org/cgi-bin/mailman/private/debbugs-submit/>
List-Post: <mailto:debbugs-submit <at> debbugs.gnu.org>
List-Help: <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=help>
List-Subscribe: <https://debbugs.gnu.org/cgi-bin/mailman/listinfo/debbugs-submit>, 
 <mailto:debbugs-submit-request <at> debbugs.gnu.org?subject=subscribe>
Errors-To: debbugs-submit-bounces <at> debbugs.gnu.org
Sender: "Debbugs-submit" <debbugs-submit-bounces <at> debbugs.gnu.org>
X-Spam-Score: -1.0 (-)

--=-=-=
Content-Type: text/plain

Tags: patch

The two patches below make Emacs use a dedicated type to represent
interpreted-function values.
    
Change `function` so that when evaluating #'(lambda ...)
we return an object of type `interpreted-function` rather than
a list starting with one of `lambda` or `closure`.
The new type reuses the existing PVEC_CLOSURE (nee PVEC_COMPILED)
tag used for byte-code function and tries to align the corresponding
elements:

- the arglist, the docstring, and the interactive-form go in the
  same slots as for byte-code functions.
- the body of the function goes in the slot used for the bytecode string.
- the lexical context goes in the slot used for the constants of
  bytecoded functions.

The first point above means that `help-function-arglist`,
`documentation`, and `interactive-form`s don't need to
distinguish interpreted and bytecode functions any more.

Main benefits of the change:

- We can now reliably distinguish a list from a function value.
  This removes some ambiguity in cases where the data can be made of
  a list or a function, such as `run-hooks` or completion tables.
- `cl-defmethod` can dispatch on `interactive-function`.
  Dispatch on `function` also works now for interpreted functions (but still
  won't work for functions represented as lists or as symbols, of course).
- Function values are now self-evaluating.  That was already the case
  when byte-compiled, but not when interpreted since
  (eval '(closure ...)) signals a void-function error.
  That also avoids false-positive warnings about "don't quote your lambdas"
  when doing things like `(mapcar ',func ...)`.


        Stefan



--=-=-=
Content-Type: text/x-diff
Content-Disposition: inline; filename=0001-COMPILED-Rename-to-CLOSURE.patch

From 669179d696ab2e3c7ab7b504dd1b6b8827bbfb0b Mon Sep 17 00:00:00 2001
From: Stefan Monnier <monnier@HIDDEN>
Date: Sun, 24 Mar 2024 18:32:25 -0400
Subject: [PATCH 1/2] (COMPILED): Rename to CLOSURE

In preparation for the use of `PVEC_COMPILED` objects for
interpreted functions, rename them to use a more neutral name.

* src/lisp.h (enum pvec_type): Rename `PVEC_COMPILED` to `PVEC_CLOSURE`.
(enum Lisp_Compiled): Use `CLOSURE_` prefix i.s.o `COMPILED_`.
Also use `CODE` rather than `BYTECODE`.
(CLOSUREP): Rename from `COMPILEDP`.
(enum Lisp_Closure): Rename from `Lisp_Compiled`.

* src/alloc.c, src/bytecode.c, src/comp.c, src/data.c, src/eval.c,
* src/fns.c, src/lisp.h, src/lread.c, src/pdumper.c, src/print.c,
* src/profiler.c: Rename all uses accordingly.
---
 src/alloc.c    | 40 ++++++++++++++++++++--------------------
 src/bytecode.c | 20 ++++++++++----------
 src/comp.c     |  2 +-
 src/data.c     | 22 +++++++++++-----------
 src/eval.c     | 26 +++++++++++++-------------
 src/fns.c      | 14 +++++++-------
 src/lisp.h     | 22 +++++++++++-----------
 src/lread.c    | 40 ++++++++++++++++++++--------------------
 src/pdumper.c  |  2 +-
 src/print.c    |  6 +++---
 src/profiler.c |  6 +++---
 11 files changed, 100 insertions(+), 100 deletions(-)

diff --git a/src/alloc.c b/src/alloc.c
index 6779d0ca9ce..a8dfde56739 100644
--- a/src/alloc.c
+++ b/src/alloc.c
@@ -3481,7 +3481,7 @@ cleanup_vector (struct Lisp_Vector *vector)
     case PVEC_XWIDGET_VIEW:
     case PVEC_TS_NODE:
     case PVEC_SQLITE:
-    case PVEC_COMPILED:
+    case PVEC_CLOSURE:
     case PVEC_CHAR_TABLE:
     case PVEC_SUB_CHAR_TABLE:
     case PVEC_RECORD:
@@ -3813,17 +3813,17 @@ and (optional) INTERACTIVE-SPEC.
 usage: (make-byte-code ARGLIST BYTE-CODE CONSTANTS DEPTH &optional DOCSTRING INTERACTIVE-SPEC &rest ELEMENTS)  */)
   (ptrdiff_t nargs, Lisp_Object *args)
 {
-  if (! ((FIXNUMP (args[COMPILED_ARGLIST])
-	  || CONSP (args[COMPILED_ARGLIST])
-	  || NILP (args[COMPILED_ARGLIST]))
-	 && STRINGP (args[COMPILED_BYTECODE])
-	 && !STRING_MULTIBYTE (args[COMPILED_BYTECODE])
-	 && VECTORP (args[COMPILED_CONSTANTS])
-	 && FIXNATP (args[COMPILED_STACK_DEPTH])))
+  if (! ((FIXNUMP (args[CLOSURE_ARGLIST])
+	  || CONSP (args[CLOSURE_ARGLIST])
+	  || NILP (args[CLOSURE_ARGLIST]))
+	 && STRINGP (args[CLOSURE_CODE])
+	 && !STRING_MULTIBYTE (args[CLOSURE_CODE])
+	 && VECTORP (args[CLOSURE_CONSTANTS])
+	 && FIXNATP (args[CLOSURE_STACK_DEPTH])))
     error ("Invalid byte-code object");
 
   /* Bytecode must be immovable.  */
-  pin_string (args[COMPILED_BYTECODE]);
+  pin_string (args[CLOSURE_CODE]);
 
   /* We used to purecopy everything here, if purify-flag was set.  This worked
      OK for Emacs-23, but with Emacs-24's lexical binding code, it can be
@@ -3833,7 +3833,7 @@ and (optional) INTERACTIVE-SPEC.
      just wasteful and other times plainly wrong (e.g. those free vars may want
      to be setcar'd).  */
   Lisp_Object val = Fvector (nargs, args);
-  XSETPVECTYPE (XVECTOR (val), PVEC_COMPILED);
+  XSETPVECTYPE (XVECTOR (val), PVEC_CLOSURE);
   return val;
 }
 
@@ -3845,12 +3845,12 @@ DEFUN ("make-closure", Fmake_closure, Smake_closure, 1, MANY, 0,
   (ptrdiff_t nargs, Lisp_Object *args)
 {
   Lisp_Object protofun = args[0];
-  CHECK_TYPE (COMPILEDP (protofun), Qbyte_code_function_p, protofun);
+  CHECK_TYPE (CLOSUREP (protofun), Qbyte_code_function_p, protofun);
 
   /* Create a copy of the constant vector, filling it with the closure
      variables in the beginning.  (The overwritten part should just
      contain placeholder values.) */
-  Lisp_Object proto_constvec = AREF (protofun, COMPILED_CONSTANTS);
+  Lisp_Object proto_constvec = AREF (protofun, CLOSURE_CONSTANTS);
   ptrdiff_t constsize = ASIZE (proto_constvec);
   ptrdiff_t nvars = nargs - 1;
   if (nvars > constsize)
@@ -3866,7 +3866,7 @@ DEFUN ("make-closure", Fmake_closure, Smake_closure, 1, MANY, 0,
   struct Lisp_Vector *v = allocate_vectorlike (protosize, false);
   v->header = XVECTOR (protofun)->header;
   memcpy (v->contents, XVECTOR (protofun)->contents, protosize * word_size);
-  v->contents[COMPILED_CONSTANTS] = constvec;
+  v->contents[CLOSURE_CONSTANTS] = constvec;
   return make_lisp_ptr (v, Lisp_Vectorlike);
 }
 
@@ -6046,7 +6046,7 @@ purecopy (Lisp_Object obj)
 
       obj = make_lisp_hash_table (purecopy_hash_table (table));
     }
-  else if (COMPILEDP (obj) || VECTORP (obj) || RECORDP (obj))
+  else if (CLOSUREP (obj) || VECTORP (obj) || RECORDP (obj))
     {
       struct Lisp_Vector *objp = XVECTOR (obj);
       ptrdiff_t nbytes = vector_nbytes (objp);
@@ -6059,7 +6059,7 @@ purecopy (Lisp_Object obj)
       for (i = 0; i < size; i++)
 	vec->contents[i] = purecopy (vec->contents[i]);
       /* Byte code strings must be pinned.  */
-      if (COMPILEDP (obj) && size >= 2 && STRINGP (vec->contents[1])
+      if (CLOSUREP (obj) && size >= 2 && STRINGP (vec->contents[1])
 	  && !STRING_MULTIBYTE (vec->contents[1]))
 	pin_string (vec->contents[1]);
       XSETVECTOR (obj, vec);
@@ -8014,11 +8014,11 @@ symbol_uses_obj (Lisp_Object symbol, Lisp_Object obj)
   return (EQ (val, obj)
 	  || EQ (sym->u.s.function, obj)
 	  || (!NILP (sym->u.s.function)
-	      && COMPILEDP (sym->u.s.function)
-	      && EQ (AREF (sym->u.s.function, COMPILED_BYTECODE), obj))
+	      && CLOSUREP (sym->u.s.function)
+	      && EQ (AREF (sym->u.s.function, CLOSURE_CODE), obj))
 	  || (!NILP (val)
-	      && COMPILEDP (val)
-	      && EQ (AREF (val, COMPILED_BYTECODE), obj)));
+	      && CLOSUREP (val)
+	      && EQ (AREF (val, CLOSURE_CODE), obj)));
 }
 
 /* Find at most FIND_MAX symbols which have OBJ as their value or
@@ -8343,7 +8343,7 @@ syms_of_alloc (void)
   enum CHECK_LISP_OBJECT_TYPE CHECK_LISP_OBJECT_TYPE;
   enum DEFAULT_HASH_SIZE DEFAULT_HASH_SIZE;
   enum Lisp_Bits Lisp_Bits;
-  enum Lisp_Compiled Lisp_Compiled;
+  enum Lisp_Closure Lisp_Closure;
   enum maxargs maxargs;
   enum MAX_ALLOCA MAX_ALLOCA;
   enum More_Lisp_Bits More_Lisp_Bits;
diff --git a/src/bytecode.c b/src/bytecode.c
index 8d7240b9966..51495d226e1 100644
--- a/src/bytecode.c
+++ b/src/bytecode.c
@@ -479,7 +479,7 @@ exec_byte_code (Lisp_Object fun, ptrdiff_t args_template,
   Lisp_Object *top = NULL;
   unsigned char const *pc = NULL;
 
-  Lisp_Object bytestr = AREF (fun, COMPILED_BYTECODE);
+  Lisp_Object bytestr = AREF (fun, CLOSURE_CODE);
 
  setup_frame: ;
   eassert (!STRING_MULTIBYTE (bytestr));
@@ -489,8 +489,8 @@ exec_byte_code (Lisp_Object fun, ptrdiff_t args_template,
      when returning, to detect unwind imbalances.  This would require adding
      a field to the frame header.  */
 
-  Lisp_Object vector = AREF (fun, COMPILED_CONSTANTS);
-  Lisp_Object maxdepth = AREF (fun, COMPILED_STACK_DEPTH);
+  Lisp_Object vector = AREF (fun, CLOSURE_CONSTANTS);
+  Lisp_Object maxdepth = AREF (fun, CLOSURE_STACK_DEPTH);
   ptrdiff_t const_length = ASIZE (vector);
   ptrdiff_t bytestr_length = SCHARS (bytestr);
   Lisp_Object *vectorp = XVECTOR (vector)->contents;
@@ -794,14 +794,14 @@ #define DEFINE(name, value) [name] = &&insn_ ## name,
 	    /* Calls to symbols-with-pos don't need to be on the fast path.  */
 	    if (BARE_SYMBOL_P (call_fun))
 	      call_fun = XBARE_SYMBOL (call_fun)->u.s.function;
-	    if (COMPILEDP (call_fun))
+	    if (CLOSUREP (call_fun))
 	      {
-		Lisp_Object template = AREF (call_fun, COMPILED_ARGLIST);
+		Lisp_Object template = AREF (call_fun, CLOSURE_ARGLIST);
 		if (FIXNUMP (template))
 		  {
 		    /* Fast path for lexbound functions.  */
 		    fun = call_fun;
-		    bytestr = AREF (call_fun, COMPILED_BYTECODE),
+		    bytestr = AREF (call_fun, CLOSURE_CODE),
 		    args_template = XFIXNUM (template);
 		    nargs = call_nargs;
 		    args = call_args;
@@ -899,8 +899,8 @@ #define DEFINE(name, value) [name] = &&insn_ ## name,
 		bc->fp = fp;
 
 		Lisp_Object fun = fp->fun;
-		Lisp_Object bytestr = AREF (fun, COMPILED_BYTECODE);
-		Lisp_Object vector = AREF (fun, COMPILED_CONSTANTS);
+		Lisp_Object bytestr = AREF (fun, CLOSURE_CODE);
+		Lisp_Object vector = AREF (fun, CLOSURE_CONSTANTS);
 		bytestr_data = SDATA (bytestr);
 		vectorp = XVECTOR (vector)->contents;
 		if (BYTE_CODE_SAFE)
@@ -976,8 +976,8 @@ #define DEFINE(name, value) [name] = &&insn_ ## name,
 		struct bc_frame *fp = bc->fp;
 
 		Lisp_Object fun = fp->fun;
-		Lisp_Object bytestr = AREF (fun, COMPILED_BYTECODE);
-		Lisp_Object vector = AREF (fun, COMPILED_CONSTANTS);
+		Lisp_Object bytestr = AREF (fun, CLOSURE_CODE);
+		Lisp_Object vector = AREF (fun, CLOSURE_CONSTANTS);
 		bytestr_data = SDATA (bytestr);
 		vectorp = XVECTOR (vector)->contents;
 		if (BYTE_CODE_SAFE)
diff --git a/src/comp.c b/src/comp.c
index 99f51e07048..d2115de522c 100644
--- a/src/comp.c
+++ b/src/comp.c
@@ -5199,7 +5199,7 @@ maybe_defer_native_compilation (Lisp_Object function_name,
   if (!native_comp_jit_compilation
       || noninteractive
       || !NILP (Vpurify_flag)
-      || !COMPILEDP (definition)
+      || !CLOSUREP (definition)
       || !STRINGP (Vload_true_file_name)
       || !suffix_p (Vload_true_file_name, ".elc")
       || !NILP (Fgethash (Vload_true_file_name, V_comp_no_native_file_h, Qnil)))
diff --git a/src/data.c b/src/data.c
index c4b9cff8ae0..681054ff8cb 100644
--- a/src/data.c
+++ b/src/data.c
@@ -248,7 +248,7 @@ DEFUN ("cl-type-of", Fcl_type_of, Scl_type_of, 1, 1, 0,
           return XSUBR (object)->max_args == UNEVALLED ? Qspecial_form
                  : SUBR_NATIVE_COMPILEDP (object) ? Qsubr_native_elisp
                  : Qprimitive_function;
-        case PVEC_COMPILED: return Qcompiled_function;
+        case PVEC_CLOSURE: return Qcompiled_function;
         case PVEC_BUFFER: return Qbuffer;
         case PVEC_CHAR_TABLE: return Qchar_table;
         case PVEC_BOOL_VECTOR: return Qbool_vector;
@@ -523,7 +523,7 @@ DEFUN ("byte-code-function-p", Fbyte_code_function_p, Sbyte_code_function_p,
        doc: /* Return t if OBJECT is a byte-compiled function object.  */)
   (Lisp_Object object)
 {
-  if (COMPILEDP (object))
+  if (CLOSUREP (object))
     return Qt;
   return Qnil;
 }
@@ -1143,19 +1143,19 @@ DEFUN ("interactive-form", Finteractive_form, Sinteractive_form, 1, 1, 0,
 		      (*spec != '(') ? build_string (spec) :
 		      Fcar (Fread_from_string (build_string (spec), Qnil, Qnil)));
     }
-  else if (COMPILEDP (fun))
+  else if (CLOSUREP (fun))
     {
-      if (PVSIZE (fun) > COMPILED_INTERACTIVE)
+      if (PVSIZE (fun) > CLOSURE_INTERACTIVE)
 	{
-	  Lisp_Object form = AREF (fun, COMPILED_INTERACTIVE);
+	  Lisp_Object form = AREF (fun, CLOSURE_INTERACTIVE);
 	  /* The vector form is the new form, where the first
 	     element is the interactive spec, and the second is the
 	     command modes. */
 	  return list2 (Qinteractive, VECTORP (form) ? AREF (form, 0) : form);
 	}
-      else if (PVSIZE (fun) > COMPILED_DOC_STRING)
+      else if (PVSIZE (fun) > CLOSURE_DOC_STRING)
         {
-          Lisp_Object doc = AREF (fun, COMPILED_DOC_STRING);
+          Lisp_Object doc = AREF (fun, CLOSURE_DOC_STRING);
           /* An invalid "docstring" is a sign that we have an OClosure.  */
           genfun = !(NILP (doc) || VALID_DOCSTRING_P (doc));
         }
@@ -1225,11 +1225,11 @@ DEFUN ("command-modes", Fcommand_modes, Scommand_modes, 1, 1, 0,
     {
       return XSUBR (fun)->command_modes;
     }
-  else if (COMPILEDP (fun))
+  else if (CLOSUREP (fun))
     {
-      if (PVSIZE (fun) <= COMPILED_INTERACTIVE)
+      if (PVSIZE (fun) <= CLOSURE_INTERACTIVE)
 	return Qnil;
-      Lisp_Object form = AREF (fun, COMPILED_INTERACTIVE);
+      Lisp_Object form = AREF (fun, CLOSURE_INTERACTIVE);
       if (VECTORP (form))
 	/* New form -- the second element is the command modes. */
 	return AREF (form, 1);
@@ -2546,7 +2546,7 @@ DEFUN ("aref", Faref, Saref, 2, 2, 0,
       ptrdiff_t size = 0;
       if (VECTORP (array))
 	size = ASIZE (array);
-      else if (COMPILEDP (array) || RECORDP (array))
+      else if (CLOSUREP (array) || RECORDP (array))
 	size = PVSIZE (array);
       else
 	wrong_type_argument (Qarrayp, array);
diff --git a/src/eval.c b/src/eval.c
index f48d7b0682f..cfed27cf629 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -2150,15 +2150,15 @@ DEFUN ("commandp", Fcommandp, Scommandp, 1, 2, 0,
         return Qt;
     }
   /* Bytecode objects are interactive if they are long enough to
-     have an element whose index is COMPILED_INTERACTIVE, which is
+     have an element whose index is CLOSURE_INTERACTIVE, which is
      where the interactive spec is stored.  */
-  else if (COMPILEDP (fun))
+  else if (CLOSUREP (fun))
     {
-      if (PVSIZE (fun) > COMPILED_INTERACTIVE)
+      if (PVSIZE (fun) > CLOSURE_INTERACTIVE)
         return Qt;
-      else if (PVSIZE (fun) > COMPILED_DOC_STRING)
+      else if (PVSIZE (fun) > CLOSURE_DOC_STRING)
         {
-          Lisp_Object doc = AREF (fun, COMPILED_DOC_STRING);
+          Lisp_Object doc = AREF (fun, CLOSURE_DOC_STRING);
           /* An invalid "docstring" is a sign that we have an OClosure.  */
           genfun = !(NILP (doc) || VALID_DOCSTRING_P (doc));
         }
@@ -2566,7 +2566,7 @@ eval_sub (Lisp_Object form)
 	    }
 	}
     }
-  else if (COMPILEDP (fun)
+  else if (CLOSUREP (fun)
 	   || SUBR_NATIVE_COMPILED_DYNP (fun)
 	   || MODULE_FUNCTIONP (fun))
     return apply_lambda (fun, original_args, count);
@@ -2944,7 +2944,7 @@ FUNCTIONP (Lisp_Object object)
 
   if (SUBRP (object))
     return XSUBR (object)->max_args != UNEVALLED;
-  else if (COMPILEDP (object) || MODULE_FUNCTIONP (object))
+  else if (CLOSUREP (object) || MODULE_FUNCTIONP (object))
     return true;
   else if (CONSP (object))
     {
@@ -2966,7 +2966,7 @@ funcall_general (Lisp_Object fun, ptrdiff_t numargs, Lisp_Object *args)
 
   if (SUBRP (fun) && !SUBR_NATIVE_COMPILED_DYNP (fun))
     return funcall_subr (XSUBR (fun), numargs, args);
-  else if (COMPILEDP (fun)
+  else if (CLOSUREP (fun)
 	   || SUBR_NATIVE_COMPILED_DYNP (fun)
 	   || MODULE_FUNCTIONP (fun))
     return funcall_lambda (fun, numargs, args);
@@ -3184,9 +3184,9 @@ funcall_lambda (Lisp_Object fun, ptrdiff_t nargs,
       else
 	xsignal1 (Qinvalid_function, fun);
     }
-  else if (COMPILEDP (fun))
+  else if (CLOSUREP (fun))
     {
-      syms_left = AREF (fun, COMPILED_ARGLIST);
+      syms_left = AREF (fun, CLOSURE_ARGLIST);
       /* Bytecode objects using lexical binding have an integral
 	 ARGLIST slot value: pass the arguments to the byte-code
 	 engine directly.  */
@@ -3314,7 +3314,7 @@ DEFUN ("func-arity", Ffunc_arity, Sfunc_arity, 1, 1, 0,
 
   if (SUBRP (function))
     result = Fsubr_arity (function);
-  else if (COMPILEDP (function))
+  else if (CLOSUREP (function))
     result = lambda_arity (function);
 #ifdef HAVE_MODULES
   else if (MODULE_FUNCTIONP (function))
@@ -3362,9 +3362,9 @@ lambda_arity (Lisp_Object fun)
       else
 	xsignal1 (Qinvalid_function, fun);
     }
-  else if (COMPILEDP (fun))
+  else if (CLOSUREP (fun))
     {
-      syms_left = AREF (fun, COMPILED_ARGLIST);
+      syms_left = AREF (fun, CLOSURE_ARGLIST);
       if (FIXNUMP (syms_left))
         return get_byte_code_arity (syms_left);
     }
diff --git a/src/fns.c b/src/fns.c
index db5e856d5bd..e987d64319f 100644
--- a/src/fns.c
+++ b/src/fns.c
@@ -152,7 +152,7 @@ DEFUN ("length", Flength, Slength, 1, 1, 0,
     val = MAX_CHAR;
   else if (BOOL_VECTOR_P (sequence))
     val = bool_vector_size (sequence);
-  else if (COMPILEDP (sequence) || RECORDP (sequence))
+  else if (CLOSUREP (sequence) || RECORDP (sequence))
     val = PVSIZE (sequence);
   else
     wrong_type_argument (Qsequencep, sequence);
@@ -1054,7 +1054,7 @@ concat_to_list (ptrdiff_t nargs, Lisp_Object *args, Lisp_Object last_tail)
       else if (NILP (arg))
 	;
       else if (VECTORP (arg) || STRINGP (arg)
-	       || BOOL_VECTOR_P (arg) || COMPILEDP (arg))
+	       || BOOL_VECTOR_P (arg) || CLOSUREP (arg))
 	{
 	  ptrdiff_t arglen = XFIXNUM (Flength (arg));
 	  ptrdiff_t argindex_byte = 0;
@@ -1114,7 +1114,7 @@ concat_to_vector (ptrdiff_t nargs, Lisp_Object *args)
     {
       Lisp_Object arg = args[i];
       if (!(VECTORP (arg) || CONSP (arg) || NILP (arg) || STRINGP (arg)
-	    || BOOL_VECTOR_P (arg) || COMPILEDP (arg)))
+	    || BOOL_VECTOR_P (arg) || CLOSUREP (arg)))
 	wrong_type_argument (Qsequencep, arg);
       EMACS_INT len = XFIXNAT (Flength (arg));
       result_len += len;
@@ -1170,7 +1170,7 @@ concat_to_vector (ptrdiff_t nargs, Lisp_Object *args)
 	}
       else
 	{
-	  eassert (COMPILEDP (arg));
+	  eassert (CLOSUREP (arg));
 	  ptrdiff_t size = PVSIZE (arg);
 	  memcpy (dst, XVECTOR (arg)->contents, size * sizeof *dst);
 	  dst += size;
@@ -2949,7 +2949,7 @@ internal_equal (Lisp_Object o1, Lisp_Object o2, enum equal_kind equal_kind,
 	if (size & PSEUDOVECTOR_FLAG)
 	  {
 	    if (((size & PVEC_TYPE_MASK) >> PSEUDOVECTOR_AREA_BITS)
-		< PVEC_COMPILED)
+		< PVEC_CLOSURE)
 	      return false;
 	    size &= PSEUDOVECTOR_SIZE_MASK;
 	  }
@@ -3346,7 +3346,7 @@ mapcar1 (EMACS_INT leni, Lisp_Object *vals, Lisp_Object fn, Lisp_Object seq)
 	  tail = XCDR (tail);
 	}
     }
-  else if (VECTORP (seq) || COMPILEDP (seq))
+  else if (VECTORP (seq) || CLOSUREP (seq))
     {
       for (ptrdiff_t i = 0; i < leni; i++)
 	{
@@ -5512,7 +5512,7 @@ sxhash_obj (Lisp_Object obj, int depth)
     case Lisp_Vectorlike:
       {
 	enum pvec_type pvec_type = PSEUDOVECTOR_TYPE (XVECTOR (obj));
-	if (! (PVEC_NORMAL_VECTOR < pvec_type && pvec_type < PVEC_COMPILED))
+	if (! (PVEC_NORMAL_VECTOR < pvec_type && pvec_type < PVEC_CLOSURE))
 	  {
 	    /* According to the CL HyperSpec, two arrays are equal only if
 	       they are 'eq', except for strings and bit-vectors.  In
diff --git a/src/lisp.h b/src/lisp.h
index 3cb4361e75e..526248dd2ba 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -1049,7 +1049,7 @@ DEFINE_GDB_SYMBOL_END (PSEUDOVECTOR_FLAG)
   PVEC_SQLITE,
 
   /* These should be last, for internal_equal and sxhash_obj.  */
-  PVEC_COMPILED,
+  PVEC_CLOSURE,
   PVEC_CHAR_TABLE,
   PVEC_SUB_CHAR_TABLE,
   PVEC_RECORD,
@@ -3223,16 +3223,16 @@ XFLOAT_DATA (Lisp_Object f)
 #define IEEE_FLOATING_POINT (FLT_RADIX == 2 && FLT_MANT_DIG == 24 \
 			     && FLT_MIN_EXP == -125 && FLT_MAX_EXP == 128)
 
-/* Meanings of slots in a Lisp_Compiled:  */
+/* Meanings of slots in a Lisp_Closure:  */
 
-enum Lisp_Compiled
+enum Lisp_Closure
   {
-    COMPILED_ARGLIST = 0,
-    COMPILED_BYTECODE = 1,
-    COMPILED_CONSTANTS = 2,
-    COMPILED_STACK_DEPTH = 3,
-    COMPILED_DOC_STRING = 4,
-    COMPILED_INTERACTIVE = 5
+    CLOSURE_ARGLIST = 0,
+    CLOSURE_CODE = 1,
+    CLOSURE_CONSTANTS = 2,
+    CLOSURE_STACK_DEPTH = 3,
+    CLOSURE_DOC_STRING = 4,
+    CLOSURE_INTERACTIVE = 5
   };
 
 /* Flag bits in a character.  These also get used in termhooks.h.
@@ -3307,9 +3307,9 @@ WINDOW_CONFIGURATIONP (Lisp_Object a)
 }
 
 INLINE bool
-COMPILEDP (Lisp_Object a)
+CLOSUREP (Lisp_Object a)
 {
-  return PSEUDOVECTORP (a, PVEC_COMPILED);
+  return PSEUDOVECTORP (a, PVEC_CLOSURE);
 }
 
 INLINE bool
diff --git a/src/lread.c b/src/lread.c
index 09a5589fd0c..8b614e6220e 100644
--- a/src/lread.c
+++ b/src/lread.c
@@ -3498,52 +3498,52 @@ bytecode_from_rev_list (Lisp_Object elems, Lisp_Object readcharfun)
   Lisp_Object *vec = XVECTOR (obj)->contents;
   ptrdiff_t size = ASIZE (obj);
 
-  if (infile && size >= COMPILED_CONSTANTS)
+  if (infile && size >= CLOSURE_CONSTANTS)
     {
       /* Always read 'lazily-loaded' bytecode (generated by the
          `byte-compile-dynamic' feature prior to Emacs 30) eagerly, to
          avoid code in the fast path during execution.  */
-      if (CONSP (vec[COMPILED_BYTECODE])
-          && FIXNUMP (XCDR (vec[COMPILED_BYTECODE])))
-        vec[COMPILED_BYTECODE] = get_lazy_string (vec[COMPILED_BYTECODE]);
+      if (CONSP (vec[CLOSURE_CODE])
+          && FIXNUMP (XCDR (vec[CLOSURE_CODE])))
+        vec[CLOSURE_CODE] = get_lazy_string (vec[CLOSURE_CODE]);
 
       /* Lazily-loaded bytecode is represented by the constant slot being nil
          and the bytecode slot a (lazily loaded) string containing the
          print representation of (BYTECODE . CONSTANTS).  Unpack the
          pieces by coerceing the string to unibyte and reading the result.  */
-      if (NILP (vec[COMPILED_CONSTANTS]) && STRINGP (vec[COMPILED_BYTECODE]))
+      if (NILP (vec[CLOSURE_CONSTANTS]) && STRINGP (vec[CLOSURE_CODE]))
         {
-          Lisp_Object enc = vec[COMPILED_BYTECODE];
+          Lisp_Object enc = vec[CLOSURE_CODE];
           Lisp_Object pair = Fread (Fcons (enc, readcharfun));
           if (!CONSP (pair))
 	    invalid_syntax ("Invalid byte-code object", readcharfun);
 
-          vec[COMPILED_BYTECODE] = XCAR (pair);
-          vec[COMPILED_CONSTANTS] = XCDR (pair);
+          vec[CLOSURE_CODE] = XCAR (pair);
+          vec[CLOSURE_CONSTANTS] = XCDR (pair);
         }
     }
 
-  if (!(size >= COMPILED_STACK_DEPTH + 1 && size <= COMPILED_INTERACTIVE + 1
-	&& (FIXNUMP (vec[COMPILED_ARGLIST])
-	    || CONSP (vec[COMPILED_ARGLIST])
-	    || NILP (vec[COMPILED_ARGLIST]))
-	&& STRINGP (vec[COMPILED_BYTECODE])
-	&& VECTORP (vec[COMPILED_CONSTANTS])
-	&& FIXNATP (vec[COMPILED_STACK_DEPTH])))
+  if (!(size >= CLOSURE_STACK_DEPTH + 1 && size <= CLOSURE_INTERACTIVE + 1
+	&& (FIXNUMP (vec[CLOSURE_ARGLIST])
+	    || CONSP (vec[CLOSURE_ARGLIST])
+	    || NILP (vec[CLOSURE_ARGLIST]))
+	&& STRINGP (vec[CLOSURE_CODE])
+	&& VECTORP (vec[CLOSURE_CONSTANTS])
+	&& FIXNATP (vec[CLOSURE_STACK_DEPTH])))
     invalid_syntax ("Invalid byte-code object", readcharfun);
 
-  if (STRING_MULTIBYTE (vec[COMPILED_BYTECODE]))
+  if (STRING_MULTIBYTE (vec[CLOSURE_CODE]))
     /* BYTESTR must have been produced by Emacs 20.2 or earlier
        because it produced a raw 8-bit string for byte-code and
        now such a byte-code string is loaded as multibyte with
        raw 8-bit characters converted to multibyte form.
        Convert them back to the original unibyte form.  */
-    vec[COMPILED_BYTECODE] = Fstring_as_unibyte (vec[COMPILED_BYTECODE]);
+    vec[CLOSURE_CODE] = Fstring_as_unibyte (vec[CLOSURE_CODE]);
 
   /* Bytecode must be immovable.  */
-  pin_string (vec[COMPILED_BYTECODE]);
+  pin_string (vec[CLOSURE_CODE]);
 
-  XSETPVECTYPE (XVECTOR (obj), PVEC_COMPILED);
+  XSETPVECTYPE (XVECTOR (obj), PVEC_CLOSURE);
   return obj;
 }
 
@@ -4678,7 +4678,7 @@ substitute_object_recurse (struct subst *subst, Lisp_Object subtree)
 	if (BOOL_VECTOR_P (subtree))
 	  return subtree;		/* No sub-objects anyway.  */
 	else if (CHAR_TABLE_P (subtree) || SUB_CHAR_TABLE_P (subtree)
-		 || COMPILEDP (subtree) || HASH_TABLE_P (subtree)
+		 || CLOSUREP (subtree) || HASH_TABLE_P (subtree)
 		 || RECORDP (subtree))
 	  length = PVSIZE (subtree);
 	else if (VECTORP (subtree))
diff --git a/src/pdumper.c b/src/pdumper.c
index ac8bf6f31f4..2963efc56ab 100644
--- a/src/pdumper.c
+++ b/src/pdumper.c
@@ -3068,7 +3068,7 @@ dump_vectorlike (struct dump_context *ctx,
         error_unsupported_dump_object(ctx, lv, "font");
       FALLTHROUGH;
     case PVEC_NORMAL_VECTOR:
-    case PVEC_COMPILED:
+    case PVEC_CLOSURE:
     case PVEC_CHAR_TABLE:
     case PVEC_SUB_CHAR_TABLE:
     case PVEC_RECORD:
diff --git a/src/print.c b/src/print.c
index 0d867b89395..612d63b7e94 100644
--- a/src/print.c
+++ b/src/print.c
@@ -1299,7 +1299,7 @@ #define PRINT_CIRCLE_CANDIDATE_P(obj)			   \
   (STRINGP (obj)                                           \
    || CONSP (obj)					   \
    || (VECTORLIKEP (obj)				   \
-       && (VECTORP (obj) || COMPILEDP (obj)		   \
+       && (VECTORP (obj) || CLOSUREP (obj)		   \
 	   || CHAR_TABLE_P (obj) || SUB_CHAR_TABLE_P (obj) \
 	   || HASH_TABLE_P (obj) || FONTP (obj)		   \
 	   || RECORDP (obj)))				   \
@@ -2091,7 +2091,7 @@ print_vectorlike_unreadable (Lisp_Object obj, Lisp_Object printcharfun,
     /* Types handled earlier.  */
     case PVEC_NORMAL_VECTOR:
     case PVEC_RECORD:
-    case PVEC_COMPILED:
+    case PVEC_CLOSURE:
     case PVEC_CHAR_TABLE:
     case PVEC_SUB_CHAR_TABLE:
     case PVEC_HASH_TABLE:
@@ -2559,7 +2559,7 @@ print_object (Lisp_Object obj, Lisp_Object printcharfun, bool escapeflag)
 	  print_stack_push_vector ("#s(", ")", obj, 0, PVSIZE (obj),
 				   printcharfun);
 	  goto next_obj;
-	case PVEC_COMPILED:
+	case PVEC_CLOSURE:
 	  print_stack_push_vector ("#[", "]", obj, 0, PVSIZE (obj),
 				   printcharfun);
 	  goto next_obj;
diff --git a/src/profiler.c b/src/profiler.c
index 5a6a8b48f6b..ac23a97b672 100644
--- a/src/profiler.c
+++ b/src/profiler.c
@@ -170,7 +170,7 @@ trace_hash (Lisp_Object *trace, int depth)
     {
       Lisp_Object f = trace[i];
       EMACS_UINT hash1
-	= (COMPILEDP (f) ? XHASH (AREF (f, COMPILED_BYTECODE))
+	= (CLOSUREP (f) ? XHASH (AREF (f, CLOSURE_CODE))
 	   : (CONSP (f) && CONSP (XCDR (f)) && BASE_EQ (Qclosure, XCAR (f)))
 	   ? XHASH (XCDR (XCDR (f))) : XHASH (f));
       hash = sxhash_combine (hash, hash1);
@@ -675,8 +675,8 @@ DEFUN ("function-equal", Ffunction_equal, Sfunction_equal, 2, 2, 0,
   bool res;
   if (EQ (f1, f2))
     res = true;
-  else if (COMPILEDP (f1) && COMPILEDP (f2))
-    res = EQ (AREF (f1, COMPILED_BYTECODE), AREF (f2, COMPILED_BYTECODE));
+  else if (CLOSUREP (f1) && CLOSUREP (f2))
+    res = EQ (AREF (f1, CLOSURE_CODE), AREF (f2, CLOSURE_CODE));
   else if (CONSP (f1) && CONSP (f2) && CONSP (XCDR (f1)) && CONSP (XCDR (f2))
 	   && EQ (Qclosure, XCAR (f1))
 	   && EQ (Qclosure, XCAR (f2)))
-- 
2.43.0


--=-=-=
Content-Type: text/patch
Content-Disposition: attachment;
 filename=0002-Use-a-dedicated-type-to-represent-interpreted-functi.patch

From 95620b74383f762b6905140a21916bd195d6677f Mon Sep 17 00:00:00 2001
From: Stefan Monnier <monnier@HIDDEN>
Date: Mon, 11 Mar 2024 16:12:26 -0400
Subject: [PATCH 2/2] Use a dedicated type to represent interpreted-function
 values

Change `function` so that when evaluating #'(lambda ...)
we return an object of type `interpreted-function` rather than
a list starting with one of `lambda` or `closure`.
The new type reuses the existing PVEC_CLOSURE (nee PVEC_COMPILED)
tag and tries to align the corresponding elements:

- the arglist, the docstring, and the interactive-form go in the
  same slots as for byte-code functions.
- the body of the function goes in the slot used for the bytecode string.
- the lexical context goes in the slot used for the constants of
  bytecoded functions.

The first point above means that `help-function-arglist`,
`documentation`, and `interactive-form`s don't need to
distinguish interpreted and bytecode functions any more.

Main benefits of the change:

- We can now reliably distinguish a list from a function value.
- `cl-defmethod` can dispatch on `interactive-function` and `closure`.
  Dispatch on `function` also works now for interpreted functions but still
  won't work for functions represented as lists or as symbols, of course.
- Function values are now self-evaluating.  That was alrready the case
  when byte-compiled, but not when interpreted since
  (eval '(closure ...)) signals a void-function error.
  That also avoids false-positive warnings about "don't quote your lambdas"
  when doing things like `(mapcar ',func ...)`.

* src/eval.c (Fmake_interpreted_closure): New function.
(Ffunction): Use it and change calling convention of
`Vinternal_make_interpreted_closure_function`.
(FUNCTIONP, Fcommandp, eval_sub, funcall_general, funcall_lambda)
(Ffunc_arity, lambda_arity): Simplify.
(funcall_lambda): Adjust to new representation.
(syms_of_eval): `defsubr` the new function.  Remove definition of `Qclosure`.

* lisp/emacs-lisp/cconv.el (cconv-make-interpreted-closure):
Change calling convention and use `make-interpreted-closure`.

* src/data.c (Fcl_type_of): Distinguish `byte-code-function`s from
`interpreted-function`s.
(Fclosurep, finterpreted_function_p): New functions.
(Fbyte_code_function_p): Don't be confused by `interpreted-function`s.
(Finteractive_form, Fcommand_modes): Simplify.
(syms_of_data): Define new type symbols and `defsubr` the two
new functions.

* lisp/emacs-lisp/cl-print.el (cl-print-object) <interpreted-function>:
New method.

* lisp/emacs-lisp/oclosure.el (oclosure): Refine the parent
to be `closure`.
(oclosure--fix-type, oclosure-type): Simplify.
(oclosure--copy, oclosure--get, oclosure--set): Adjust to
new representation.

* src/callint.c (Fcall_interactively): Adjust to new representation.

* src/lread.c (bytecode_from_rev_list):
* src/alloc.c (Fmake_byte_code): Loosen sanity checks to allow building
`interpreted-function`s as well.

* lisp/simple.el (function-documentation):
* lisp/help.el (help-function-arglist): Remove the old `closure` case
and adjust the byte-code case so it handles `interpreted-function`s.

* lisp/emacs-lisp/cl-preloaded.el (closure): New type.
(byte-code-function): Add it as a parent.
(interpreted-function): Adjust parent (the type itself was already
added earlier by accident).

* lisp/emacs-lisp/bytecomp.el (byte-compile--reify-function): Adjust to
new representation.
(byte-compile): Use `interpreted-function-p`.

* lisp/emacs-lisp/byte-opt.el (byte-compile-inline-expand): Adjust to
new representation.
(side-effect-free-fns): Add `interpreted-function-p` and `closurep`.

* src/profiler.c (trace_hash, ffunction_equal): Simplify.
* lisp/profiler.el (profiler-function-equal): Simplify.

* lisp/emacs-lisp/nadvice.el (advice--interactive-form-1):
Use `interpreted-function-p`; adjust to new representation; and take
advantage of the fact that function values are now self-evaluating.

* lisp/emacs-lisp/lisp-mode.el (closure):
Remove `lisp-indent-function` property.

* lisp/emacs-lisp/disass.el (disassemble-internal): Adjust to
new representation.
* lisp/emacs-lisp/edebug.el (edebug--strip-instrumentation):
Use `interpreted-function-p`.
* lisp/emacs-lisp/comp-common.el (comp-known-type-specifiers):
Add `closurep` and `interpreted-function-p`.

* test/lisp/help-fns-tests.el (help-fns-test-lisp-defun): Adjust to
more precise type info in `describe-function`.
* test/lisp/erc/resources/erc-d/erc-d-tests.el (erc-d--render-entries):
Use `interpreted-function-p`.
* test/lisp/emacs-lisp/macroexp-resources/vk.el (vk-f4, vk-f5):
Don't hardcode function values.

* doc/lispref/functions.texi (Anonymous Functions): Don't suggest that
function values are lists.  Reword "self-quoting" to reflect the
fact that #' doesn't return the exact same object.  Update examples
with the new shape of the return value.

* doc/lispref/variables.texi (Lexical Binding):
* doc/lispref/lists.texi (Rearrangement):
* doc/lispref/control.texi (Handling Errors): Update examples to reflect
new representation of function values.
---
 doc/lispref/control.texi                      |   2 +-
 doc/lispref/functions.texi                    |  24 +--
 doc/lispref/lists.texi                        |   4 +-
 doc/lispref/variables.texi                    |   2 +-
 etc/NEWS                                      |  12 ++
 lisp/emacs-lisp/byte-opt.el                   |   3 +-
 lisp/emacs-lisp/bytecomp.el                   |  20 ++-
 lisp/emacs-lisp/cconv.el                      |  38 +++--
 lisp/emacs-lisp/cl-preloaded.el               |   6 +-
 lisp/emacs-lisp/cl-print.el                   |  32 ++++
 lisp/emacs-lisp/comp-common.el                |   2 +
 lisp/emacs-lisp/disass.el                     |   6 +-
 lisp/emacs-lisp/edebug.el                     |   2 +-
 lisp/emacs-lisp/lisp-mode.el                  |   1 -
 lisp/emacs-lisp/nadvice.el                    |   6 +-
 lisp/emacs-lisp/oclosure.el                   |  96 +++++-------
 lisp/help.el                                  |   3 +-
 lisp/profiler.el                              |   5 +-
 lisp/simple.el                                |   5 +-
 src/alloc.c                                   |  23 +--
 src/callint.c                                 |   6 +-
 src/data.c                                    |  41 +++--
 src/eval.c                                    | 142 +++++++++++-------
 src/lread.c                                   |  35 +++--
 src/profiler.c                                |   8 +-
 test/lisp/emacs-lisp/macroexp-resources/vk.el |  48 +++---
 test/lisp/erc/resources/erc-d/erc-d-tests.el  |   5 +-
 test/lisp/help-fns-tests.el                   |  10 +-
 28 files changed, 345 insertions(+), 242 deletions(-)

diff --git a/doc/lispref/control.texi b/doc/lispref/control.texi
index f9f3389c398..46024e2fdee 100644
--- a/doc/lispref/control.texi
+++ b/doc/lispref/control.texi
@@ -2411,7 +2411,7 @@ Handling Errors
 @group
 Debugger entered--Lisp error: (error "Oops")
   signal(error ("Oops"))
-  (closure (t) (err) (signal 'error (cdr err)))((user-error "Oops"))
+  #f(lambda (err) [t] (signal 'error (cdr err)))((user-error "Oops"))
   user-error("Oops")
   @dots{}
   eval((handler-bind ((user-error (lambda (err) @dots{}
diff --git a/doc/lispref/functions.texi b/doc/lispref/functions.texi
index ff635fc54b2..0062de91354 100644
--- a/doc/lispref/functions.texi
+++ b/doc/lispref/functions.texi
@@ -1136,8 +1136,7 @@ Anonymous Functions
 of this.
 
   When defining a lambda expression that is to be used as an anonymous
-function, you can in principle use any method to construct the list.
-But typically you should use the @code{lambda} macro, or the
+function, you should use the @code{lambda} macro, or the
 @code{function} special form, or the @code{#'} read syntax:
 
 @defmac lambda args [doc] [interactive] body@dots{}
@@ -1145,17 +1144,18 @@ Anonymous Functions
 @var{args}, documentation string @var{doc} (if any), interactive spec
 @var{interactive} (if any), and body forms given by @var{body}.
 
-Under dynamic binding, this macro effectively makes @code{lambda}
-forms self-quoting: evaluating a form whose @sc{car} is @code{lambda}
-yields the form itself:
+For example, this macro makes @code{lambda} forms almost self-quoting:
+evaluating a form whose @sc{car} is @code{lambda} yields a value that is
+almost like the form itself:
 
 @example
 (lambda (x) (* x x))
-     @result{} (lambda (x) (* x x))
+     @result{} #f(lambda (x) :dynbind (* x x))
 @end example
 
-Note that when evaluating under lexical binding the result is a
-closure object (@pxref{Closures}).
+When evaluating under lexical binding the result is a similar
+closure object, where the @code{:dynbind} marker is replaced by the
+captured variables (@pxref{Closures}).
 
 The @code{lambda} form has one other effect: it tells the Emacs
 evaluator and byte-compiler that its argument is a function, by using
@@ -1164,8 +1164,8 @@ Anonymous Functions
 
 @defspec function function-object
 @cindex function quoting
-This special form returns @var{function-object} without evaluating it.
-In this, it is similar to @code{quote} (@pxref{Quoting}).  But unlike
+This special form returns the function value of the @var{function-object}.
+In many ways, it is similar to @code{quote} (@pxref{Quoting}).  But unlike
 @code{quote}, it also serves as a note to the Emacs evaluator and
 byte-compiler that @var{function-object} is intended to be used as a
 function.  Assuming @var{function-object} is a valid lambda
@@ -1495,7 +1495,7 @@ Function Cells
 @group
 (defun bar (n) (+ n 2))
 (symbol-function 'bar)
-     @result{} (lambda (n) (+ n 2))
+     @result{} #f(lambda (n) [t] (+ n 2))
 @end group
 @group
 (fset 'baz 'bar)
@@ -1608,7 +1608,7 @@ Closures
 @example
 ;; @r{lexical binding is enabled.}
 (lambda (x) (* x x))
-     @result{} (closure (t) (x) (* x x))
+     @result{} #f(lambda (x) [t] (* x x))
 @end example
 
 @noindent
diff --git a/doc/lispref/lists.texi b/doc/lispref/lists.texi
index 1409e51c0d4..06472539744 100644
--- a/doc/lispref/lists.texi
+++ b/doc/lispref/lists.texi
@@ -1249,7 +1249,7 @@ Rearrangement
 
 @group
 (symbol-function 'add-foo)
-     @result{} (lambda (x) (nconc '(foo) x))
+     @result{} #f(lambda (x) [t] (nconc '(foo) x))
 @end group
 
 @group
@@ -1267,7 +1267,7 @@ Rearrangement
 
 @group
 (symbol-function 'add-foo)
-     @result{} (lambda (x) (nconc '(foo 1 2 3 4) x))
+     @result{} #f(lambda (x) [t] (nconc '(foo 1 2 3 4) x))
 @end group
 @end smallexample
 @end defun
diff --git a/doc/lispref/variables.texi b/doc/lispref/variables.texi
index 4d61d461deb..16b6b52e5f1 100644
--- a/doc/lispref/variables.texi
+++ b/doc/lispref/variables.texi
@@ -1079,7 +1079,7 @@ Lexical Binding
 (let ((x 0))             ; @r{@code{x} is lexically bound.}
   (setq my-ticker (lambda ()
                     (setq x (1+ x)))))
-    @result{} (closure ((x . 0)) ()
+    @result{} #f(lambda () [(x 0)]
           (setq x (1+ x)))
 
 (funcall my-ticker)
diff --git a/etc/NEWS b/etc/NEWS
index 7a73815179c..b7cd929af04 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -1680,6 +1680,18 @@ documentation and examples.
 
 * Incompatible Lisp Changes in Emacs 30.1
 
++++
+** Evaluating a 'lambda' returns an object of type 'interpreted-function'.
+Instead of representing interpreted functions as lists that start with
+either 'lambda' or 'closure', Emacs now represents them as objects
+of their own 'interpreted-function' type, which is very similar
+to 'byte-code-function' objects (the argument list, docstring, and
+interactive forms are placed in the same slots).
+Lists that start with 'lambda' are now used only for non-evaluated
+functions (in other words, for source code), but for backward compatibility
+reasons, 'functionp' still recognizes them as functions and you can
+still call them as before.
+
 +++
 ** 'define-globalized-minor-mode' requires that modes use 'run-mode-hooks'.
 Minor modes defined with 'define-globalized-minor-mode', such as
diff --git a/lisp/emacs-lisp/byte-opt.el b/lisp/emacs-lisp/byte-opt.el
index ea163723a3e..3d6b35422b8 100644
--- a/lisp/emacs-lisp/byte-opt.el
+++ b/lisp/emacs-lisp/byte-opt.el
@@ -164,7 +164,7 @@ byte-compile-inline-expand
        ;; The byte-code will be really inlined in byte-compile-unfold-bcf.
        (byte-compile--check-arity-bytecode form fn)
        `(,fn ,@(cdr form)))
-      ((or `(lambda . ,_) `(closure . ,_))
+      ((pred interpreted-function-p)
        ;; While byte-compile-unfold-bcf can inline dynbind byte-code into
        ;; letbind byte-code (or any other combination for that matter), we
        ;; can only inline dynbind source into dynbind source or lexbind
@@ -1870,6 +1870,7 @@ byte-optimize-set
          charsetp
          ;; data.c
          arrayp atom bare-symbol-p bool-vector-p bufferp byte-code-function-p
+         interpreted-function-p closurep
          byteorder car-safe cdr-safe char-or-string-p char-table-p
          condition-variable-p consp eq floatp indirect-function
          integer-or-marker-p integerp keywordp listp markerp
diff --git a/lisp/emacs-lisp/bytecomp.el b/lisp/emacs-lisp/bytecomp.el
index fb3278c08ab..c82f8ba6ae2 100644
--- a/lisp/emacs-lisp/bytecomp.el
+++ b/lisp/emacs-lisp/bytecomp.el
@@ -2900,9 +2900,14 @@ byte-compile-output-as-comment
 (defun byte-compile--reify-function (fun)
   "Return an expression which will evaluate to a function value FUN.
 FUN should be an interpreted closure."
-  (pcase-let* ((`(closure ,env ,args . ,body) fun)
-               (`(,preamble . ,body) (macroexp-parse-body body))
-               (renv ()))
+  (let* ((args (aref fun 0))
+         (body (aref fun 1))
+         (env (aref fun 2))
+         (docstring (function-documentation fun))
+         (iform (interactive-form fun))
+         (preamble `(,@(if docstring (list docstring))
+                     ,@(if iform (list iform))))
+         (renv ()))
     ;; Turn the function's closed vars (if any) into local let bindings.
     (dolist (binding env)
       (cond
@@ -2939,11 +2944,11 @@ byte-compile
                  (if (symbolp form) form "provided"))
         fun)
        (t
-        (when (or (symbolp form) (eq (car-safe fun) 'closure))
+        (when (or (symbolp form) (interpreted-function-p fun))
           ;; `fun' is a function *value*, so try to recover its
           ;; corresponding source code.
-          (when (setq lexical-binding (eq (car-safe fun) 'closure))
-            (setq fun (byte-compile--reify-function fun)))
+          (setq lexical-binding (not (null (aref fun 2))))
+          (setq fun (byte-compile--reify-function fun))
           (setq need-a-value t))
         ;; Expand macros.
         (setq fun (byte-compile-preprocess fun))
@@ -5133,7 +5138,6 @@ byte-compile-file-form-defalias
             ;; `arglist' is the list of arguments (or t if not recognized).
             ;; `body' is the body of `lam' (or t if not recognized).
             ((or `(lambda ,arglist . ,body)
-                 ;; `(closure ,_ ,arglist . ,body)
                  (and `(internal-make-closure ,arglist . ,_) (let body t))
                  (and (let arglist t) (let body t)))
              lam))
@@ -5571,7 +5575,7 @@ display-call-tree
 		     " <compiled macro>"
 		   " <macro>"))
 		((eq 'lambda (car f))
-		 "<function>")
+		 "<function-like list>")
 		(t "???"))
 	  (format " (%d callers + %d calls = %d)"
 		  ;; Does the optimizer eliminate common subexpressions?-sk
diff --git a/lisp/emacs-lisp/cconv.el b/lisp/emacs-lisp/cconv.el
index 4ff47971351..e6a78f07762 100644
--- a/lisp/emacs-lisp/cconv.el
+++ b/lisp/emacs-lisp/cconv.el
@@ -902,7 +902,7 @@ cconv-fv
                                     (delete-dups cconv--dynbindings)))))
         (cons fvs dyns)))))
 
-(defun cconv-make-interpreted-closure (fun env)
+(defun cconv-make-interpreted-closure (args body env docstring iform)
   "Make a closure for the interpreter.
 This is intended to be called at runtime by the ELisp interpreter (when
 the code has not been compiled).
@@ -911,22 +911,27 @@ cconv-make-interpreted-closure
 i.e. a list whose elements can be either plain symbols (which indicate
 that this symbol should use dynamic scoping) or pairs (SYMBOL . VALUE)
 for the lexical bindings."
-  (cl-assert (eq (car-safe fun) 'lambda))
+  (cl-assert (consp body))
+  (cl-assert (listp args))
   (let ((lexvars (delq nil (mapcar #'car-safe env))))
-    (if (or (null lexvars)
-            ;; Functions with a `:closure-dont-trim-context' marker
-            ;; should keep their whole context untrimmed (bug#59213).
-            (and (eq :closure-dont-trim-context (nth 2 fun))
-                 ;; Check the function doesn't just return the magic keyword.
-                 (nthcdr 3 fun)))
+    (if (or
+         ;; Functions with a `:closure-dont-trim-context' marker
+         ;; should keep their whole context untrimmed (bug#59213).
+         (and (eq :closure-dont-trim-context (car body))
+              ;; Check the function doesn't just return the magic keyword.
+              (cdr body)
+              ;; Drop the magic marker from the closure.
+              (setq body (cdr body)))
+         ;; There's no var to capture, so skip the analysis.
+         (null lexvars))
         ;; The lexical environment is empty, or needs to be preserved,
         ;; so there's no need to look for free variables.
-        ;; Attempting to replace ,(cdr fun) by a macroexpanded version
-        ;; causes bootstrap to fail.
-        `(closure ,env . ,(cdr fun))
+        ;; Attempting to replace body by a macroexpanded version
+        ;; caused bootstrap to fail.
+        (make-interpreted-closure args body env docstring iform)
       ;; We could try and cache the result of the macroexpansion and
       ;; `cconv-fv' analysis.  Not sure it's worth the trouble.
-      (let* ((form `#',fun)
+      (let* ((form `#'(lambda ,args ,iform . ,body))
              (expanded-form
               (let ((lexical-binding t) ;; Tell macros which dialect is in use.
 	            ;; Make the macro aware of any defvar declarations in scope.
@@ -935,10 +940,10 @@ cconv-make-interpreted-closure
                          (append env macroexp--dynvars) env)))
                 (macroexpand-all form macroexpand-all-environment)))
              ;; Since we macroexpanded the body, we may as well use that.
-             (expanded-fun-cdr
+             (expanded-fun-body
               (pcase expanded-form
-                (`#'(lambda . ,cdr) cdr)
-                (_ (cdr fun))))
+                (`#'(lambda ,_args ,_iform . ,newbody) newbody)
+                (_ body)))
 
              (dynvars (delq nil (mapcar (lambda (b) (if (symbolp b) b)) env)))
              (fvs (cconv-fv expanded-form lexvars dynvars))
@@ -946,7 +951,8 @@ cconv-make-interpreted-closure
                             (cdr fvs))))
         ;; Never return a nil env, since nil means to use the dynbind
         ;; dialect of ELisp.
-        `(closure ,(or newenv '(t)) . ,expanded-fun-cdr)))))
+        (make-interpreted-closure args expanded-fun-body (or newenv '(t))
+                                  docstring iform)))))
 
 
 (provide 'cconv)
diff --git a/lisp/emacs-lisp/cl-preloaded.el b/lisp/emacs-lisp/cl-preloaded.el
index 83d9e6ee220..c202f1d1064 100644
--- a/lisp/emacs-lisp/cl-preloaded.el
+++ b/lisp/emacs-lisp/cl-preloaded.el
@@ -444,13 +444,15 @@ function
   )
 (cl--define-built-in-type compiled-function (function)
   "Abstract type of functions that have been compiled.")
-(cl--define-built-in-type byte-code-function (compiled-function)
+(cl--define-built-in-type closure (function)
+  "Abstract type of functions represented by a vector-like object.")
+(cl--define-built-in-type byte-code-function (compiled-function closure)
   "Type of functions that have been byte-compiled.")
 (cl--define-built-in-type subr (atom)
   "Abstract type of functions compiled to machine code.")
 (cl--define-built-in-type module-function (function)
   "Type of functions provided via the module API.")
-(cl--define-built-in-type interpreted-function (function)
+(cl--define-built-in-type interpreted-function (closure)
   "Type of functions that have not been compiled.")
 (cl--define-built-in-type special-form (subr)
   "Type of the core syntactic elements of the Emacs Lisp language.")
diff --git a/lisp/emacs-lisp/cl-print.el b/lisp/emacs-lisp/cl-print.el
index 5e5eee1da9e..3a8f80f6e93 100644
--- a/lisp/emacs-lisp/cl-print.el
+++ b/lisp/emacs-lisp/cl-print.el
@@ -237,6 +237,38 @@ cl-print-object
                               'byte-code-function object)))))
     (princ ")" stream)))
 
+(cl-defmethod cl-print-object ((object interpreted-function) stream)
+  (unless stream (setq stream standard-output))
+  (princ "#f(lambda " stream)
+  (let ((args (help-function-arglist object 'preserve-names)))
+    ;; It's tempting to print the arglist from the "usage" info in the
+    ;; doc (e.g. for `&key` args), but that only makes sense if we
+    ;; *don't* print the body, since otherwise the body will tend to
+    ;; refer to args that don't appear in the arglist.
+    (if args
+        (prin1 args stream)
+      (princ "()" stream)))
+  (let ((env (aref object 2)))
+    (if (null env)
+        (princ " :dynbind" stream)
+      (princ " " stream)
+      (cl-print-object
+       (vconcat (mapcar (lambda (x) (if (consp x) (list (car x) (cdr x)) x))
+                        env))
+       stream)))
+  (let* ((doc (documentation object 'raw)))
+    (when doc
+      (princ " " stream)
+      (prin1 doc stream)))
+  (let ((inter (interactive-form object)))
+    (when inter
+      (princ " " stream)
+      (cl-print-object inter stream)))
+  (dolist (exp (aref object 1))
+    (princ " " stream)
+    (cl-print-object exp stream))
+  (princ ")" stream))
+
 ;; This belongs in oclosure.el, of course, but some load-ordering issues make it
 ;; complicated.
 (cl-defmethod cl-print-object ((object accessor) stream)
diff --git a/lisp/emacs-lisp/comp-common.el b/lisp/emacs-lisp/comp-common.el
index 4edfe811586..62fd28f772e 100644
--- a/lisp/emacs-lisp/comp-common.el
+++ b/lisp/emacs-lisp/comp-common.el
@@ -118,7 +118,9 @@ comp-known-type-specifiers
     (buffer-substring
      (function ((or integer marker) (or integer marker)) string))
     (bufferp (function (t) boolean))
+    (closurep (function (t) boolean))
     (byte-code-function-p (function (t) boolean))
+    (interpreted-function-p (function (t) boolean))
     (capitalize (function ((or integer string)) (or integer string)))
     (car (function (list) t))
     (car-less-than-car (function (list list) boolean))
diff --git a/lisp/emacs-lisp/disass.el b/lisp/emacs-lisp/disass.el
index 850cc2085f7..15caee9b29c 100644
--- a/lisp/emacs-lisp/disass.el
+++ b/lisp/emacs-lisp/disass.el
@@ -129,7 +129,7 @@ disassemble-internal
 	   (setq args (help-function-arglist obj))	;save arg list
 	   (setq obj (cdr obj))		;throw lambda away
 	   (setq obj (cdr obj)))
-	  ((byte-code-function-p obj)
+	  ((closurep obj)
 	   (setq args (help-function-arglist obj)))
           (t (error "Compilation failed")))
     (if (zerop indent) ; not a nested function
@@ -178,7 +178,9 @@ disassemble-internal
 	  (t
 	   (insert "Uncompiled body:  ")
 	   (let ((print-escape-newlines t))
-	     (prin1 (macroexp-progn obj)
+	     (prin1 (macroexp-progn (if (interpreted-function-p obj)
+		                        (aref obj 1)
+		                      obj))
 		    (current-buffer))))))
   (if interactive-p
       (message "")))
diff --git a/lisp/emacs-lisp/edebug.el b/lisp/emacs-lisp/edebug.el
index b27ffbca908..3414bb592c0 100644
--- a/lisp/emacs-lisp/edebug.el
+++ b/lisp/emacs-lisp/edebug.el
@@ -4254,7 +4254,7 @@ edebug--strip-instrumentation
           ((pred edebug--symbol-prefixed-p) nil)
           (_
            (when (and skip-next-lambda
-                      (not (memq (car-safe fun) '(closure lambda))))
+                      (not (interpreted-function-p fun)))
              (warn "Edebug--strip-instrumentation expected an interpreted function:\n%S" fun))
 	   (unless skip-next-lambda
              (edebug--unwrap-frame new-frame)
diff --git a/lisp/emacs-lisp/lisp-mode.el b/lisp/emacs-lisp/lisp-mode.el
index 3475d944337..601cc7bf712 100644
--- a/lisp/emacs-lisp/lisp-mode.el
+++ b/lisp/emacs-lisp/lisp-mode.el
@@ -1347,7 +1347,6 @@ lisp-indent-defform
 (put 'condition-case 'lisp-indent-function 2)
 (put 'handler-case 'lisp-indent-function 1) ;CL
 (put 'unwind-protect 'lisp-indent-function 1)
-(put 'closure 'lisp-indent-function 2)
 
 (defun indent-sexp (&optional endpos)
   "Indent each line of the list starting just after point.
diff --git a/lisp/emacs-lisp/nadvice.el b/lisp/emacs-lisp/nadvice.el
index 5326c520601..36df143a82a 100644
--- a/lisp/emacs-lisp/nadvice.el
+++ b/lisp/emacs-lisp/nadvice.el
@@ -185,7 +185,7 @@ advice-eval-interactive-spec
 (defun advice--interactive-form-1 (function)
   "Like `interactive-form' but preserves the static context if needed."
   (let ((if (interactive-form function)))
-    (if (or (null if) (not (eq 'closure (car-safe function))))
+    (if (not (and if (interpreted-function-p function)))
         if
       (cl-assert (eq 'interactive (car if)))
       (let ((form (cadr if)))
@@ -193,14 +193,14 @@ advice--interactive-form-1
             if
           ;; The interactive is expected to be run in the static context
           ;; that the function captured.
-          (let ((ctx (nth 1 function)))
+          (let ((ctx (aref function 2)))
             `(interactive
               ,(let* ((f (if (eq 'function (car-safe form)) (cadr form) form)))
                  ;; If the form jut returns a function, preserve the fact that
                  ;; it just returns a function, which is an info we use in
                  ;; `advice--make-interactive-form'.
                  (if (eq 'lambda (car-safe f))
-                     `',(eval form ctx)
+                     (eval form ctx)
                    `(eval ',form ',ctx))))))))))
 
 (defun advice--interactive-form (function)
diff --git a/lisp/emacs-lisp/oclosure.el b/lisp/emacs-lisp/oclosure.el
index 4da8e61aaa7..165d7c4b6e8 100644
--- a/lisp/emacs-lisp/oclosure.el
+++ b/lisp/emacs-lisp/oclosure.el
@@ -146,7 +146,7 @@ oclosure--index-table
 (setf (cl--find-class 'oclosure)
       (oclosure--class-make 'oclosure
                             "The root parent of all OClosure types"
-                            nil (list (cl--find-class 'function))
+                            nil (list (cl--find-class 'closure))
                             '(oclosure)))
 (defun oclosure--p (oclosure)
   (not (not (oclosure-type oclosure))))
@@ -431,75 +431,57 @@ oclosure-lambda
 
 (defun oclosure--fix-type (_ignore oclosure)
   "Helper function to implement `oclosure-lambda' via a macro.
-This has 2 uses:
-- For interpreted code, this converts the representation of type information
-  by moving it from the docstring to the environment.
-- For compiled code, this is used as a marker which cconv uses to check that
-  immutable fields are indeed not mutated."
-  (if (byte-code-function-p oclosure)
-      ;; Actually, this should never happen since `cconv.el' should have
-      ;; optimized away the call to this function.
-      oclosure
-    ;; For byte-coded functions, we store the type as a symbol in the docstring
-    ;; slot.  For interpreted functions, there's no specific docstring slot
-    ;; so `Ffunction' turns the symbol into a string.
-    ;; We thus have convert it back into a symbol (via `intern') and then
-    ;; stuff it into the environment part of the closure with a special
-    ;; marker so we can distinguish this entry from actual variables.
-    (cl-assert (eq 'closure (car-safe oclosure)))
-    (let ((typename (nth 3 oclosure))) ;; The "docstring".
-      (cl-assert (stringp typename))
-      (push (cons :type (intern typename))
-            (cadr oclosure))
-      oclosure)))
+This is used as a marker which cconv uses to check that
+immutable fields are indeed not mutated."
+  (cl-assert (closurep oclosure))
+  ;; This should happen only for interpreted closures since `cconv.el'
+  ;; should have optimized away the call to this function.
+  oclosure)
 
 (defun oclosure--copy (oclosure mutlist &rest args)
+  (cl-assert (closurep oclosure))
   (if (byte-code-function-p oclosure)
       (apply #'make-closure oclosure
              (if (null mutlist)
                  args
                (mapcar (lambda (arg) (if (pop mutlist) (list arg) arg)) args)))
-    (cl-assert (eq 'closure (car-safe oclosure))
-               nil "oclosure not closure: %S" oclosure)
-    (cl-assert (eq :type (caar (cadr oclosure))))
-    (let ((env (cadr oclosure)))
-      `(closure
-           (,(car env)
-            ,@(named-let loop ((env (cdr env)) (args args))
-                (when args
-                  (cons (cons (caar env) (car args))
-                        (loop (cdr env) (cdr args)))))
-            ,@(nthcdr (1+ (length args)) env))
-           ,@(nthcdr 2 oclosure)))))
+    (cl-assert (consp (aref oclosure 1)))
+    (cl-assert (null (aref oclosure 3)))
+    (cl-assert (symbolp (aref oclosure 4)))
+    (let ((env (aref oclosure 2)))
+      (make-interpreted-closure
+       (aref oclosure 0)
+       (aref oclosure 1)
+       (named-let loop ((env env) (args args))
+         (if (null args) env
+           (cons (cons (caar env) (car args))
+                 (loop (cdr env) (cdr args)))))
+       (aref oclosure 4)
+       (if (> (length oclosure) 5)
+           `(interactive ,(aref oclosure 5)))))))
 
 (defun oclosure--get (oclosure index mutable)
-  (if (byte-code-function-p oclosure)
-      (let* ((csts (aref oclosure 2))
-             (v (aref csts index)))
-        (if mutable (car v) v))
-    (cl-assert (eq 'closure (car-safe oclosure)))
-    (cl-assert (eq :type (caar (cadr oclosure))))
-    (cdr (nth (1+ index) (cadr oclosure)))))
+  (cl-assert (closurep oclosure))
+  (let* ((csts (aref oclosure 2)))
+    (if (vectorp csts)
+        (let ((v (aref csts index)))
+          (if mutable (car v) v))
+      (cdr (nth index csts)))))
 
 (defun oclosure--set (v oclosure index)
-  (if (byte-code-function-p oclosure)
-      (let* ((csts (aref oclosure 2))
-             (cell (aref csts index)))
-        (setcar cell v))
-    (cl-assert (eq 'closure (car-safe oclosure)))
-    (cl-assert (eq :type (caar (cadr oclosure))))
-    (setcdr (nth (1+ index) (cadr oclosure)) v)))
+  (cl-assert (closurep oclosure))
+  (let ((csts (aref oclosure 2)))
+    (if (vectorp csts)
+        (let ((cell (aref csts index)))
+          (setcar cell v))
+      (setcdr (nth index csts) v))))
 
 (defun oclosure-type (oclosure)
-  "Return the type of OCLOSURE, or nil if the arg is not a OClosure."
-  (if (byte-code-function-p oclosure)
-      (let ((type (and (> (length oclosure) 4) (aref oclosure 4))))
-        (if (symbolp type) type))
-    (and (eq 'closure (car-safe oclosure))
-         (let* ((env (car-safe (cdr oclosure)))
-                (first-var (car-safe env)))
-           (and (eq :type (car-safe first-var))
-                (cdr first-var))))))
+  "Return the type of OCLOSURE, or nil if the arg is not an OClosure."
+  (and (closurep oclosure)
+       (> (length oclosure) 4)
+       (let ((type (aref oclosure 4)))
+         (if (symbolp type) type))))
 
 (defconst oclosure--accessor-prototype
   ;; Use `oclosure--lambda' to circumvent a bootstrapping problem:
diff --git a/lisp/help.el b/lisp/help.el
index d4e39f04e53..10bd2ffec3f 100644
--- a/lisp/help.el
+++ b/lisp/help.el
@@ -2349,9 +2349,8 @@ help-function-arglist
   ;; If definition is a macro, find the function inside it.
   (if (eq (car-safe def) 'macro) (setq def (cdr def)))
   (cond
-   ((and (byte-code-function-p def) (listp (aref def 0))) (aref def 0))
+   ((and (closurep def) (listp (aref def 0))) (aref def 0))
    ((eq (car-safe def) 'lambda) (nth 1 def))
-   ((eq (car-safe def) 'closure) (nth 2 def))
    ((and (featurep 'native-compile)
          (subrp def)
          (listp (subr-native-lambda-list def)))
diff --git a/lisp/profiler.el b/lisp/profiler.el
index 4e02cd1d890..eb72f128c07 100644
--- a/lisp/profiler.el
+++ b/lisp/profiler.el
@@ -275,10 +275,7 @@ profiler-calltree-build-1
 
 
 (define-hash-table-test 'profiler-function-equal #'function-equal
-  (lambda (f) (cond
-          ((byte-code-function-p f) (aref f 1))
-          ((eq (car-safe f) 'closure) (cddr f))
-          (t f))))
+                        (lambda (f) (if (closurep f) (aref f 1) f)))
 
 (defun profiler-calltree-build-unified (tree log)
   ;; Let's try to unify all those partial backtraces into a single
diff --git a/lisp/simple.el b/lisp/simple.el
index e4629ce3db7..be64f3574e0 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -2703,15 +2703,14 @@ function-documentation
                        (or (stringp doc)
                            (fixnump doc) (fixnump (cdr-safe doc))))))
     (pcase function
-      ((pred byte-code-function-p)
+      ((pred closurep)
        (when (> (length function) 4)
          (let ((doc (aref function 4)))
            (when (funcall docstring-p doc) doc))))
       ((or (pred stringp) (pred vectorp)) "Keyboard macro.")
       (`(keymap . ,_)
        "Prefix command (definition is a keymap associating keystrokes with commands).")
-      ((or `(lambda ,_args . ,body) `(closure ,_env ,_args . ,body)
-           `(autoload ,_file . ,body))
+      ((or `(lambda ,_args . ,body) `(autoload ,_file . ,body))
        (let ((doc (car body)))
 	 (when (funcall docstring-p doc)
            doc)))
diff --git a/src/alloc.c b/src/alloc.c
index a8dfde56739..b0e5d52cc32 100644
--- a/src/alloc.c
+++ b/src/alloc.c
@@ -3813,17 +3813,22 @@ and (optional) INTERACTIVE-SPEC.
 usage: (make-byte-code ARGLIST BYTE-CODE CONSTANTS DEPTH &optional DOCSTRING INTERACTIVE-SPEC &rest ELEMENTS)  */)
   (ptrdiff_t nargs, Lisp_Object *args)
 {
-  if (! ((FIXNUMP (args[CLOSURE_ARGLIST])
-	  || CONSP (args[CLOSURE_ARGLIST])
-	  || NILP (args[CLOSURE_ARGLIST]))
-	 && STRINGP (args[CLOSURE_CODE])
-	 && !STRING_MULTIBYTE (args[CLOSURE_CODE])
-	 && VECTORP (args[CLOSURE_CONSTANTS])
-	 && FIXNATP (args[CLOSURE_STACK_DEPTH])))
+  if (CONSP (args[CLOSURE_CODE]))
+    ;                           /* An interpreted closure.  */
+  else if ((FIXNUMP (args[CLOSURE_ARGLIST])
+	    || CONSP (args[CLOSURE_ARGLIST])
+	    || NILP (args[CLOSURE_ARGLIST]))
+	   && STRINGP (args[CLOSURE_CODE])
+	   && !STRING_MULTIBYTE (args[CLOSURE_CODE])
+	   && VECTORP (args[CLOSURE_CONSTANTS])
+	   && FIXNATP (args[CLOSURE_STACK_DEPTH]))
+    {
+      /* Bytecode must be immovable.  */
+      pin_string (args[CLOSURE_CODE]);
+    }
+  else
     error ("Invalid byte-code object");
 
-  /* Bytecode must be immovable.  */
-  pin_string (args[CLOSURE_CODE]);
 
   /* We used to purecopy everything here, if purify-flag was set.  This worked
      OK for Emacs-23, but with Emacs-24's lexical binding code, it can be
diff --git a/src/callint.c b/src/callint.c
index b31faba8704..9d6f2ab2888 100644
--- a/src/callint.c
+++ b/src/callint.c
@@ -319,10 +319,10 @@ DEFUN ("call-interactively", Fcall_interactively, Scall_interactively, 1, 3, 0,
     {
       Lisp_Object funval = Findirect_function (function, Qt);
       uintmax_t events = num_input_events;
+      Lisp_Object env = CLOSUREP (funval) && CONSP (AREF (funval, CLOSURE_CODE))
+		        ? AREF (funval, CLOSURE_CONSTANTS) : Qnil;
       /* Compute the arg values using the user's expression.  */
-      specs = Feval (specs,
- 		     CONSP (funval) && EQ (Qclosure, XCAR (funval))
-		     ? CAR_SAFE (XCDR (funval)) : Qnil);
+      specs = Feval (specs, env);
       if (events != num_input_events || !NILP (record_flag))
 	{
 	  /* We should record this command on the command history.
diff --git a/src/data.c b/src/data.c
index 681054ff8cb..4d1fdf0c7fd 100644
--- a/src/data.c
+++ b/src/data.c
@@ -248,7 +248,9 @@ DEFUN ("cl-type-of", Fcl_type_of, Scl_type_of, 1, 1, 0,
           return XSUBR (object)->max_args == UNEVALLED ? Qspecial_form
                  : SUBR_NATIVE_COMPILEDP (object) ? Qsubr_native_elisp
                  : Qprimitive_function;
-        case PVEC_CLOSURE: return Qcompiled_function;
+        case PVEC_CLOSURE:
+          return CONSP (AREF (object, CLOSURE_CODE))
+                 ? Qinterpreted_function : Qbyte_code_function;
         case PVEC_BUFFER: return Qbuffer;
         case PVEC_CHAR_TABLE: return Qchar_table;
         case PVEC_BOOL_VECTOR: return Qbool_vector;
@@ -518,12 +520,32 @@ DEFUN ("subrp", Fsubrp, Ssubrp, 1, 1, 0,
   return Qnil;
 }
 
+DEFUN ("closurep", Fclosurep, Sclosurep,
+       1, 1, 0,
+       doc: /* Return t if OBJECT is a function object.  */)
+  (Lisp_Object object)
+{
+  if (CLOSUREP (object))
+    return Qt;
+  return Qnil;
+}
+
 DEFUN ("byte-code-function-p", Fbyte_code_function_p, Sbyte_code_function_p,
        1, 1, 0,
        doc: /* Return t if OBJECT is a byte-compiled function object.  */)
   (Lisp_Object object)
 {
-  if (CLOSUREP (object))
+  if (CLOSUREP (object) && STRINGP (AREF (object, CLOSURE_CODE)))
+    return Qt;
+  return Qnil;
+}
+
+DEFUN ("interpreted-function-p", Finterpreted_function_p,
+       Sinterpreted_function_p, 1, 1, 0,
+       doc: /* Return t if OBJECT is an interpreted function value.  */)
+  (Lisp_Object object)
+{
+  if (CLOSUREP (object) && CONSP (AREF (object, CLOSURE_CODE)))
     return Qt;
   return Qnil;
 }
@@ -1174,12 +1196,9 @@ DEFUN ("interactive-form", Finteractive_form, Sinteractive_form, 1, 1, 0,
   else if (CONSP (fun))
     {
       Lisp_Object funcar = XCAR (fun);
-      if (EQ (funcar, Qclosure)
-	  || EQ (funcar, Qlambda))
+      if (EQ (funcar, Qlambda))
 	{
 	  Lisp_Object form = Fcdr (XCDR (fun));
-	  if (EQ (funcar, Qclosure))
-	    form = Fcdr (form);
 	  Lisp_Object spec = Fassq (Qinteractive, form);
 	  if (NILP (spec) && VALID_DOCSTRING_P (CAR_SAFE (form)))
             /* A "docstring" is a sign that we may have an OClosure.  */
@@ -1257,12 +1276,9 @@ DEFUN ("command-modes", Fcommand_modes, Scommand_modes, 1, 1, 0,
   else if (CONSP (fun))
     {
       Lisp_Object funcar = XCAR (fun);
-      if (EQ (funcar, Qclosure)
-	  || EQ (funcar, Qlambda))
+      if (EQ (funcar, Qlambda))
 	{
 	  Lisp_Object form = Fcdr (XCDR (fun));
-	  if (EQ (funcar, Qclosure))
-	    form = Fcdr (form);
 	  return Fcdr (Fcdr (Fassq (Qinteractive, form)));
 	}
     }
@@ -4224,7 +4240,8 @@ #define PUT_ERROR(sym, tail, msg)			\
   DEFSYM (Qspecial_form, "special-form");
   DEFSYM (Qprimitive_function, "primitive-function");
   DEFSYM (Qsubr_native_elisp, "subr-native-elisp");
-  DEFSYM (Qcompiled_function, "compiled-function");
+  DEFSYM (Qbyte_code_function, "byte-code-function");
+  DEFSYM (Qinterpreted_function, "interpreted-function");
   DEFSYM (Qbuffer, "buffer");
   DEFSYM (Qframe, "frame");
   DEFSYM (Qvector, "vector");
@@ -4289,6 +4306,8 @@ #define PUT_ERROR(sym, tail, msg)			\
   defsubr (&Smarkerp);
   defsubr (&Ssubrp);
   defsubr (&Sbyte_code_function_p);
+  defsubr (&Sinterpreted_function_p);
+  defsubr (&Sclosurep);
   defsubr (&Smodule_function_p);
   defsubr (&Schar_or_string_p);
   defsubr (&Sthreadp);
diff --git a/src/eval.c b/src/eval.c
index cfed27cf629..9a2c767ffee 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -510,6 +510,32 @@ DEFUN ("quote", Fquote, Squote, 1, UNEVALLED, 0,
   return XCAR (args);
 }
 
+DEFUN ("make-interpreted-closure", Fmake_interpreted_closure,
+       Smake_interpreted_closure, 3, 5, 0,
+       doc: /* Make an interpreted closure.
+ARGS should be the list of formal arguments.
+BODY should be a non-empty list of forms.
+ENV should be a lexical environment, like the second argument of `eval'.
+IFORM if non-nil should be of the form (interactive ...).  */)
+  (Lisp_Object args, Lisp_Object body, Lisp_Object env,
+   Lisp_Object docstring, Lisp_Object iform)
+{
+  CHECK_CONS (body);          /* Make sure it's not confused with byte-code! */
+  if (!NILP (iform))
+    {
+      iform = Fcdr (iform);
+      return CALLN (Fmake_byte_code,
+                    args, body, env, Qnil, docstring,
+                    NILP (Fcdr (iform))
+                    ? Fcar (iform)
+                    : CALLN (Fvector, XCAR (iform), XCDR (iform)));
+    }
+  else if (!NILP (docstring))
+    return CALLN (Fmake_byte_code, args, body, env, Qnil, docstring);
+  else
+    return CALLN (Fmake_byte_code, args, body, env);
+}
+
 DEFUN ("function", Ffunction, Sfunction, 1, UNEVALLED, 0,
        doc: /* Like `quote', but preferred for objects which are functions.
 In byte compilation, `function' causes its argument to be handled by
@@ -525,33 +551,55 @@ DEFUN ("function", Ffunction, Sfunction, 1, UNEVALLED, 0,
   if (!NILP (XCDR (args)))
     xsignal2 (Qwrong_number_of_arguments, Qfunction, Flength (args));
 
-  if (!NILP (Vinternal_interpreter_environment)
-      && CONSP (quoted)
+  if (CONSP (quoted)
       && EQ (XCAR (quoted), Qlambda))
     { /* This is a lambda expression within a lexical environment;
 	 return an interpreted closure instead of a simple lambda.  */
       Lisp_Object cdr = XCDR (quoted);
-      Lisp_Object tmp = cdr;
-      if (CONSP (tmp)
-	  && (tmp = XCDR (tmp), CONSP (tmp))
-	  && (tmp = XCAR (tmp), CONSP (tmp))
-	  && (EQ (QCdocumentation, XCAR (tmp))))
-	{ /* Handle the special (:documentation <form>) to build the docstring
+      Lisp_Object args = Fcar (cdr);
+      cdr = Fcdr (cdr);
+      Lisp_Object docstring = Qnil, iform = Qnil;
+      if (CONSP (cdr))
+        {
+          docstring = XCAR (cdr);
+          if (STRINGP (docstring))
+            {
+              Lisp_Object tmp = XCDR (cdr);
+              if (!NILP (tmp))
+                cdr = tmp;
+              else     /* It's not a docstring, it's a return value.  */
+                docstring = Qnil;
+            }
+          /* Handle the special (:documentation <form>) to build the docstring
 	     dynamically.  */
-	  Lisp_Object docstring = eval_sub (Fcar (XCDR (tmp)));
-	  if (SYMBOLP (docstring) && !NILP (docstring))
-	    /* Hack for OClosures: Allow the docstring to be a symbol
-             * (the OClosure's type).  */
-	    docstring = Fsymbol_name (docstring);
-	  CHECK_STRING (docstring);
-	  cdr = Fcons (XCAR (cdr), Fcons (docstring, XCDR (XCDR (cdr))));
-	}
-      if (NILP (Vinternal_make_interpreted_closure_function))
-        return Fcons (Qclosure, Fcons (Vinternal_interpreter_environment, cdr));
+          else if (CONSP (docstring)
+                   && EQ (QCdocumentation, XCAR (docstring))
+                   && (docstring = eval_sub (Fcar (XCDR (docstring))),
+                       true))
+            cdr = XCDR (cdr);
+          else
+            docstring = Qnil;   /* Not a docstring after all.  */
+        }
+      if (CONSP (cdr))
+        {
+          iform = XCAR (cdr);
+          if (CONSP (iform)
+              && EQ (Qinteractive, XCAR (iform)))
+            cdr = XCDR (cdr);
+          else
+            iform = Qnil;   /* Not an interactive-form after all.  */
+        }
+      if (NILP (cdr))
+        cdr = Fcons (Qnil, Qnil); /* Make sure the body is never empty! */
+
+      if (NILP (Vinternal_interpreter_environment)
+          || NILP (Vinternal_make_interpreted_closure_function))
+        return Fmake_interpreted_closure
+            (args, cdr, Vinternal_interpreter_environment, docstring, iform);
       else
-        return call2 (Vinternal_make_interpreted_closure_function,
-                      Fcons (Qlambda, cdr),
-                      Vinternal_interpreter_environment);
+        return call5 (Vinternal_make_interpreted_closure_function,
+                      args, cdr, Vinternal_interpreter_environment,
+                      docstring, iform);
     }
   else
     /* Simply quote the argument.  */
@@ -2192,9 +2240,7 @@ DEFUN ("commandp", Fcommandp, Scommandp, 1, 2, 0,
       else
         {
           Lisp_Object body = CDR_SAFE (XCDR (fun));
-          if (EQ (funcar, Qclosure))
-            body = CDR_SAFE (body);
-          else if (!EQ (funcar, Qlambda))
+          if (!EQ (funcar, Qlambda))
 	    return Qnil;
 	  if (!NILP (Fassq (Qinteractive, body)))
 	    return Qt;
@@ -2610,8 +2656,7 @@ eval_sub (Lisp_Object form)
 	  exp = unbind_to (count1, exp);
 	  val = eval_sub (exp);
 	}
-      else if (EQ (funcar, Qlambda)
-	       || EQ (funcar, Qclosure))
+      else if (EQ (funcar, Qlambda))
 	return apply_lambda (fun, original_args, count);
       else
 	xsignal1 (Qinvalid_function, original_fun);
@@ -2949,7 +2994,7 @@ FUNCTIONP (Lisp_Object object)
   else if (CONSP (object))
     {
       Lisp_Object car = XCAR (object);
-      return EQ (car, Qlambda) || EQ (car, Qclosure);
+      return EQ (car, Qlambda);
     }
   else
     return false;
@@ -2979,8 +3024,7 @@ funcall_general (Lisp_Object fun, ptrdiff_t numargs, Lisp_Object *args)
       Lisp_Object funcar = XCAR (fun);
       if (!SYMBOLP (funcar))
 	xsignal1 (Qinvalid_function, original_fun);
-      if (EQ (funcar, Qlambda)
-	  || EQ (funcar, Qclosure))
+      if (EQ (funcar, Qlambda))
 	return funcall_lambda (fun, numargs, args);
       else if (EQ (funcar, Qautoload))
 	{
@@ -3168,16 +3212,7 @@ funcall_lambda (Lisp_Object fun, ptrdiff_t nargs,
 
   if (CONSP (fun))
     {
-      if (EQ (XCAR (fun), Qclosure))
-	{
-	  Lisp_Object cdr = XCDR (fun);	/* Drop `closure'.  */
-	  if (! CONSP (cdr))
-	    xsignal1 (Qinvalid_function, fun);
-	  fun = cdr;
-	  lexenv = XCAR (fun);
-	}
-      else
-	lexenv = Qnil;
+      lexenv = Qnil;
       syms_left = XCDR (fun);
       if (CONSP (syms_left))
 	syms_left = XCAR (syms_left);
@@ -3192,10 +3227,12 @@ funcall_lambda (Lisp_Object fun, ptrdiff_t nargs,
 	 engine directly.  */
       if (FIXNUMP (syms_left))
 	return exec_byte_code (fun, XFIXNUM (syms_left), nargs, arg_vector);
-      /* Otherwise the bytecode object uses dynamic binding and the
-	 ARGLIST slot contains a standard formal argument list whose
-	 variables are bound dynamically below.  */
-      lexenv = Qnil;
+      /* Otherwise the closure either is interpreted
+	 or uses dynamic binding and the ARGLIST slot contains a standard
+	 formal argument list whose variables are bound dynamically below.  */
+      lexenv = CONSP (AREF (fun, CLOSURE_CODE))
+               ? AREF (fun, CLOSURE_CONSTANTS)
+               : Qnil;
     }
 #ifdef HAVE_MODULES
   else if (MODULE_FUNCTIONP (fun))
@@ -3279,7 +3316,14 @@ funcall_lambda (Lisp_Object fun, ptrdiff_t nargs,
       val = XSUBR (fun)->function.a0 ();
     }
   else
-    val = exec_byte_code (fun, 0, 0, NULL);
+    {
+      eassert (CLOSUREP (fun));
+      val = CONSP (AREF (fun, CLOSURE_CODE))
+            /* Interpreted function.  */
+            ? Fprogn (AREF (fun, CLOSURE_CODE))
+            /* Dynbound bytecode.  */
+            : exec_byte_code (fun, 0, 0, NULL);
+    }
 
   return unbind_to (count, val);
 }
@@ -3329,8 +3373,7 @@ DEFUN ("func-arity", Ffunc_arity, Sfunc_arity, 1, 1, 0,
       funcar = XCAR (function);
       if (!SYMBOLP (funcar))
 	xsignal1 (Qinvalid_function, original);
-      if (EQ (funcar, Qlambda)
-	  || EQ (funcar, Qclosure))
+      if (EQ (funcar, Qlambda))
 	result = lambda_arity (function);
       else if (EQ (funcar, Qautoload))
 	{
@@ -3351,11 +3394,6 @@ lambda_arity (Lisp_Object fun)
 
   if (CONSP (fun))
     {
-      if (EQ (XCAR (fun), Qclosure))
-	{
-	  fun = XCDR (fun);	/* Drop `closure'.  */
-	  CHECK_CONS (fun);
-	}
       syms_left = XCDR (fun);
       if (CONSP (syms_left))
 	syms_left = XCAR (syms_left);
@@ -4266,7 +4304,6 @@ syms_of_eval (void)
   DEFSYM (Qcommandp, "commandp");
   DEFSYM (Qand_rest, "&rest");
   DEFSYM (Qand_optional, "&optional");
-  DEFSYM (Qclosure, "closure");
   DEFSYM (QCdocumentation, ":documentation");
   DEFSYM (Qdebug, "debug");
   DEFSYM (Qdebug_early, "debug-early");
@@ -4424,6 +4461,7 @@ syms_of_eval (void)
   defsubr (&Ssetq);
   defsubr (&Squote);
   defsubr (&Sfunction);
+  defsubr (&Smake_interpreted_closure);
   defsubr (&Sdefault_toplevel_value);
   defsubr (&Sset_default_toplevel_value);
   defsubr (&Sdefvar);
diff --git a/src/lread.c b/src/lread.c
index 8b614e6220e..983fdb883ff 100644
--- a/src/lread.c
+++ b/src/lread.c
@@ -3523,25 +3523,32 @@ bytecode_from_rev_list (Lisp_Object elems, Lisp_Object readcharfun)
         }
     }
 
-  if (!(size >= CLOSURE_STACK_DEPTH + 1 && size <= CLOSURE_INTERACTIVE + 1
+  if (!(size >= CLOSURE_STACK_DEPTH && size <= CLOSURE_INTERACTIVE + 1
 	&& (FIXNUMP (vec[CLOSURE_ARGLIST])
 	    || CONSP (vec[CLOSURE_ARGLIST])
 	    || NILP (vec[CLOSURE_ARGLIST]))
-	&& STRINGP (vec[CLOSURE_CODE])
-	&& VECTORP (vec[CLOSURE_CONSTANTS])
-	&& FIXNATP (vec[CLOSURE_STACK_DEPTH])))
+	&& ((STRINGP (vec[CLOSURE_CODE]) /* Byte-code function.  */
+	     && VECTORP (vec[CLOSURE_CONSTANTS])
+	     && size > CLOSURE_STACK_DEPTH
+	     && (FIXNATP (vec[CLOSURE_STACK_DEPTH])))
+	    || (CONSP (vec[CLOSURE_CODE]) /* Interpreted function.  */
+	        && (CONSP (vec[CLOSURE_CONSTANTS])
+	            || NILP (vec[CLOSURE_CONSTANTS]))))))
     invalid_syntax ("Invalid byte-code object", readcharfun);
 
-  if (STRING_MULTIBYTE (vec[CLOSURE_CODE]))
-    /* BYTESTR must have been produced by Emacs 20.2 or earlier
-       because it produced a raw 8-bit string for byte-code and
-       now such a byte-code string is loaded as multibyte with
-       raw 8-bit characters converted to multibyte form.
-       Convert them back to the original unibyte form.  */
-    vec[CLOSURE_CODE] = Fstring_as_unibyte (vec[CLOSURE_CODE]);
-
-  /* Bytecode must be immovable.  */
-  pin_string (vec[CLOSURE_CODE]);
+  if (STRINGP (vec[CLOSURE_CODE]))
+    {
+      if (STRING_MULTIBYTE (vec[CLOSURE_CODE]))
+        /* BYTESTR must have been produced by Emacs 20.2 or earlier
+           because it produced a raw 8-bit string for byte-code and
+           now such a byte-code string is loaded as multibyte with
+           raw 8-bit characters converted to multibyte form.
+           Convert them back to the original unibyte form.  */
+        vec[CLOSURE_CODE] = Fstring_as_unibyte (vec[CLOSURE_CODE]);
+
+      /* Bytecode must be immovable.  */
+      pin_string (vec[CLOSURE_CODE]);
+    }
 
   XSETPVECTYPE (XVECTOR (obj), PVEC_CLOSURE);
   return obj;
diff --git a/src/profiler.c b/src/profiler.c
index ac23a97b672..6e1dc46abd3 100644
--- a/src/profiler.c
+++ b/src/profiler.c
@@ -170,9 +170,7 @@ trace_hash (Lisp_Object *trace, int depth)
     {
       Lisp_Object f = trace[i];
       EMACS_UINT hash1
-	= (CLOSUREP (f) ? XHASH (AREF (f, CLOSURE_CODE))
-	   : (CONSP (f) && CONSP (XCDR (f)) && BASE_EQ (Qclosure, XCAR (f)))
-	   ? XHASH (XCDR (XCDR (f))) : XHASH (f));
+	= (CLOSUREP (f) ? XHASH (AREF (f, CLOSURE_CODE)) : XHASH (f));
       hash = sxhash_combine (hash, hash1);
     }
   return hash;
@@ -677,10 +675,6 @@ DEFUN ("function-equal", Ffunction_equal, Sfunction_equal, 2, 2, 0,
     res = true;
   else if (CLOSUREP (f1) && CLOSUREP (f2))
     res = EQ (AREF (f1, CLOSURE_CODE), AREF (f2, CLOSURE_CODE));
-  else if (CONSP (f1) && CONSP (f2) && CONSP (XCDR (f1)) && CONSP (XCDR (f2))
-	   && EQ (Qclosure, XCAR (f1))
-	   && EQ (Qclosure, XCAR (f2)))
-    res = EQ (XCDR (XCDR (f1)), XCDR (XCDR (f2)));
   else
     res = false;
   return res ? Qt : Qnil;
diff --git a/test/lisp/emacs-lisp/macroexp-resources/vk.el b/test/lisp/emacs-lisp/macroexp-resources/vk.el
index 5358bcaeb5c..c59a6b9f8f1 100644
--- a/test/lisp/emacs-lisp/macroexp-resources/vk.el
+++ b/test/lisp/emacs-lisp/macroexp-resources/vk.el
@@ -78,29 +78,31 @@ vk-f1
 
 (defconst vk-val3 (eval-when-compile (vk-f3 0)))
 
-(defconst vk-f4 '(lambda (x)
-                   (defvar vk-v4)
-                   (let ((vk-v4 31)
-                         (y 32))
-                     (ignore vk-v4 x y)
-                     (list
-                      (vk-variable-kind vk-a)   ; dyn
-                      (vk-variable-kind vk-b)   ; dyn
-                      (vk-variable-kind vk-v4)  ; dyn
-                      (vk-variable-kind x)      ; dyn
-                      (vk-variable-kind y)))))  ; dyn
-
-(defconst vk-f5 '(closure (t) (x)
-                   (defvar vk-v5)
-                   (let ((vk-v5 41)
-                         (y 42))
-                     (ignore vk-v5 x y)
-                     (list
-                      (vk-variable-kind vk-a)   ; dyn
-                      (vk-variable-kind vk-b)   ; dyn
-                      (vk-variable-kind vk-v5)  ; dyn
-                      (vk-variable-kind x)      ; lex
-                      (vk-variable-kind y)))))  ; lex
+(defconst vk-f4 (eval '(lambda (x)
+                         (defvar vk-v4)
+                         (let ((vk-v4 31)
+                               (y 32))
+                           (ignore vk-v4 x y)
+                           (list
+                            (vk-variable-kind vk-a) ; dyn
+                            (vk-variable-kind vk-b) ; dyn
+                            (vk-variable-kind vk-v4) ; dyn
+                            (vk-variable-kind x)     ; dyn
+                            (vk-variable-kind y))))  ; dyn
+                      nil))
+
+(defconst vk-f5 (eval '(lambda (x)
+                         (defvar vk-v5)
+                         (let ((vk-v5 41)
+                               (y 42))
+                           (ignore vk-v5 x y)
+                           (list
+                            (vk-variable-kind vk-a) ; dyn
+                            (vk-variable-kind vk-b) ; dyn
+                            (vk-variable-kind vk-v5) ; dyn
+                            (vk-variable-kind x)     ; lex
+                            (vk-variable-kind y))))  ; lex
+                      t))
 
 (defun vk-f6 ()
   (eval '(progn
diff --git a/test/lisp/erc/resources/erc-d/erc-d-tests.el b/test/lisp/erc/resources/erc-d/erc-d-tests.el
index 78f87399afb..dda1b1ced84 100644
--- a/test/lisp/erc/resources/erc-d/erc-d-tests.el
+++ b/test/lisp/erc/resources/erc-d/erc-d-tests.el
@@ -367,8 +367,9 @@ erc-d--render-entries
       (should (equal (funcall it) "foo3foo")))
 
     (ert-info ("Exits clean")
-      (when (listp (alist-get 'f (erc-d-dialog-vars dialog))) ; may be compiled
-        (should (eq 'closure (car (alist-get 'f (erc-d-dialog-vars dialog))))))
+      (when (interpreted-function-p
+             (alist-get 'f (erc-d-dialog-vars dialog))) ; may be compiled
+        (should (aref (alist-get 'f (erc-d-dialog-vars dialog)) 2)))
       (should-not (funcall it))
       (should (equal (erc-d-dialog-vars dialog)
                      `((:a . 1)
diff --git a/test/lisp/help-fns-tests.el b/test/lisp/help-fns-tests.el
index 1beeb77640c..82350a4bc71 100644
--- a/test/lisp/help-fns-tests.el
+++ b/test/lisp/help-fns-tests.el
@@ -63,14 +63,14 @@ help-fns-test-lisp-macro
     (should (string-match regexp result))))
 
 (ert-deftest help-fns-test-lisp-defun ()
-  (let ((regexp (if (featurep 'native-compile)
-                    "a subr-native-elisp in .+subr\\.el"
-                  "a compiled-function in .+subr\\.el"))
+  (let ((regexp "a \\([^ ]+\\) in .+subr\\.el")
         (result (help-fns-tests--describe-function 'last)))
-    (should (string-match regexp result))))
+    (should (string-match regexp result))
+    (should (member (match-string 1 result)
+                    '("subr-native-elisp" "byte-code-function")))))
 
 (ert-deftest help-fns-test-lisp-defsubst ()
-  (let ((regexp "a compiled-function in .+subr\\.el")
+  (let ((regexp "a byte-code-function in .+subr\\.el")
         (result (help-fns-tests--describe-function 'posn-window)))
     (should (string-match regexp result))))
 
-- 
2.43.0


--=-=-=--





Acknowledgement sent to Stefan Monnier <monnier@HIDDEN>:
New bug report received and forwarded. Copy sent to bug-gnu-emacs@HIDDEN. Full text available.
Report forwarded to bug-gnu-emacs@HIDDEN:
bug#70368; Package emacs. Full text available.
Please note: This is a static page, with minimal formatting, updated once a day.
Click here to see this page with the latest information and nicer formatting.
Last modified: Mon, 15 Apr 2024 11:30:14 UTC

GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997 nCipher Corporation Ltd, 1994-97 Ian Jackson.