bug-gnu-emacs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

bug#19206: 25.0.50; CC Mode tracks wrong source files


From: Sebastian Wiesner
Subject: bug#19206: 25.0.50; CC Mode tracks wrong source files
Date: Tue, 2 Dec 2014 12:03:21 +0100

> Am 30.11.2014 um 19:42 schrieb Alan Mackenzie <address@hidden>:
> 
> Hello, again, Sebastian.
> 
> On Fri, Nov 28, 2014 at 10:25:42PM -0000, Alan Mackenzie wrote:
>> Hello, Sebastian.
>> In article <address@hidden> you wrote:
>>> CC Mode tracks wrong source files when a CC Mode derived mode is
>>> installed non-interactively.
> 
>>> To reproduce, save the following code as `cc-miscompile.el'
> 
>>> (require 'package)
>>> (require 'cc-defs)
> 
>>> (defun main ()
>>> (add-to-list 'package-archives '("melpa" . "http://melpa.org/packages/";))
> 
>>> (setq package-user-dir (make-temp-file "cc-miscompile" 'directory))
> 
>>> (package-initialize)
>>> (package-refresh-contents)
>>> (package-install 'd-mode)
> 
>>> (require 'd-mode)
> 
>>> (let ((source (get (intern "c-typedef-decl-kwds" c-lang-constants) 
>>> 'source)))
>>>   (message "Sources: %S" (mapcar 'car source)))
> 
>>> (delete-directory package-user-dir 'recursive))
> 
>>> (main)
> 
>>> and run it with `emacs -Q --script cc-miscompile.el'.  The output is as
>>> follows (package.el output shortened for readility):
> 
>>> Contacting host: melpa.org:80
>>> Contacting host: elpa.gnu.org:80
>>> [?]
>>> Sources: (d-mode cc-miscompile cc-langs)
> 
>>> Note that `cc-miscompile' ends up in the source list of
>>> `c-typedef-decl-kwds', even though it never actually calls any `c-*'
>>> functions at all.
> 
> OK.  The problem was that CC Mode was using the flag `load-in-progress'
> to assume that a CC Mode file was being loaded, for example by a
> `require' form inside a compilation of another CC Mode file.  This
> assumption breaks down when another file, such as cc-miscompile.el,
> while loading, initiates compilation of a CC Mode derivative.
> 
>> The byte compilation of d-mode.el is being done during the loading of
>> cc-miscompile.el.  This somewhat unusual constellation, I think, is
>> causing the problem.  When CC Mode determines the file name to put onto
>> a c-lang-defconst's 'source property, it gives priority to the load file
>> name, and only when this is nil does it use the byte-compile file name.
>> (This is in defsubst c-get-current-file in cc-defs.el).  It would seem
>> this is not the correct priority.
> 
>> I think swapping the first two arms of the `cond' form in
>> c-get-current-file may solve the problem.  It's a bit late to try this
>> tonight, I'll try it tomorrow.
> 
> No, that wouldn't work.  What I've implemented is when both loading and
> byte-compilation are active at the same time, CC Mode walks down the
> lisp stack to discover which one of them is actually active.

Well, if you say… I find this “solution” horrifying, but I am probably just 
unable to appreciate the full complexity of this issue.

>>> Naturally, CC Mode will later try to load this file, and fail if it is
>>> not in the `load-path'.  This effectively breaks installations of D
>>> Mode from non-interactive Emacs sessions.
> 
> Please try the following patch, which seems to work in the test case you
> supplied, in the real situation, and let me know how well it works.  The
> d-mode.elc produced can be successfully loaded into the Emacs that built
> it.

Do I need to build a patched Emacs to try it?

> diff --git a/lisp/progmodes/cc-bytecomp.el b/lisp/progmodes/cc-bytecomp.el
> index 1936627..bd2fd34 100644
> --- a/lisp/progmodes/cc-bytecomp.el
> +++ b/lisp/progmodes/cc-bytecomp.el
> @@ -84,13 +84,60 @@
>   ;;`(message ,@args)
>   )
> 
> +(defun cc-bytecomp-compiling-or-loading ()
> +  ;; Return whether byte-compilation or loading is currently active,
> +  ;; returning 'compiling or 'loading or nil.
> +  ;; If both are active, the "innermost" activity counts.  Note that
> +  ;; compilation can trigger loading (various `require' type forms)
> +  ;; and loading can trigger compilation (the package manager does
> +  ;; this).  We walk the lisp stack if necessary.
> +  (cond
> +   ((and load-in-progress
> +      (boundp 'byte-compile-dest-file)
> +      (stringp byte-compile-dest-file))
> +    (let ((n 0) elt)
> +      (while (and
> +           (setq elt (backtrace-frame n))
> +           (not (and (car elt)
> +                     (memq (cadr elt)
> +                           '(load byte-compile-file
> +                                  byte-recompile-directory
> +                                  batch-byte-compile)))))
> +     (setq n (1+ n)))
> +      (cond
> +       ((eq (cadr elt) 'load)
> +     'loading)
> +       ((memq (cadr elt) '(byte-compile-file
> +                        byte-recompile-directory
> +                        batch-byte-compile))
> +     'compiling)
> +       (t                            ; Can't happen.
> +     (message "cc-bytecomp-compiling-or-loading: System flags spuriously 
> set")
> +     nil))))
> +   (load-in-progress
> +    ;; Being loaded.
> +    'loading)
> +   ((and (boundp 'byte-compile-dest-file)
> +      (stringp byte-compile-dest-file))
> +    ;; Being compiled.
> +    'compiling)
> +   (t
> +    ;; Being evaluated interactively.
> +    nil)))
> +
> +(defsubst cc-bytecomp-is-compiling ()
> +  "Return non-nil if eval'ed during compilation."
> +  (eq (cc-bytecomp-compiling-or-loading) 'compiling))
> +
> +(defsubst cc-bytecomp-is-loading ()
> +  "Return non-nil if eval'ed during loading.
> +Nil will be returned if we're in a compilation triggered by the loading."
> +  (eq (cc-bytecomp-compiling-or-loading) 'loading))
> +
> (defun cc-bytecomp-setup-environment ()
>   ;; Eval'ed during compilation to setup variables, functions etc
>   ;; declared with `cc-bytecomp-defvar' et al.
> -  (if (not load-in-progress)
> -      ;; Look at `load-in-progress' to tell whether we're called
> -      ;; directly in the file being compiled or just from some file
> -      ;; being loaded during compilation.
> +  (if (not (cc-bytecomp-is-loading))
>       (let (p)
>       (if cc-bytecomp-environment-set
>           (error "Byte compilation environment already set - \
> @@ -138,7 +185,7 @@ perhaps a `cc-bytecomp-restore-environment' is forgotten 
> somewhere"))
> (defun cc-bytecomp-restore-environment ()
>   ;; Eval'ed during compilation to restore variables, functions etc
>   ;; declared with `cc-bytecomp-defvar' et al.
> -  (if (not load-in-progress)
> +  (if (not (cc-bytecomp-is-loading))
>       (let (p)
>       (setq p cc-bytecomp-unbound-variables)
>       (while p
> @@ -282,9 +329,7 @@ use within `eval-when-compile'."
>   `(eval-when-compile
>      (if (and (fboundp 'cc-bytecomp-is-compiling)
>             (cc-bytecomp-is-compiling))
> -      (if (or (not load-in-progress)
> -              (not (featurep ,cc-part)))
> -          (cc-bytecomp-load (symbol-name ,cc-part)))
> +          (cc-bytecomp-load (symbol-name ,cc-part))
>        (require ,cc-part))))
> 
> (defmacro cc-external-require (feature)
> @@ -296,12 +341,6 @@ afterwards.  Don't use within `eval-when-compile'."
>      (require ,feature)
>      (eval-when-compile (cc-bytecomp-setup-environment))))
> 
> -(defun cc-bytecomp-is-compiling ()
> -  "Return non-nil if eval'ed during compilation.  Don't use outside
> -`eval-when-compile'."
> -  (and (boundp 'byte-compile-dest-file)
> -       (stringp byte-compile-dest-file)))
> -
> (defmacro cc-bytecomp-defvar (var)
>   "Binds the symbol as a variable during compilation of the file,
> to silence the byte compiler.  Don't use within `eval-when-compile'."
> @@ -315,8 +354,7 @@ to silence the byte compiler.  Don't use within 
> `eval-when-compile'."
>             "cc-bytecomp-defvar: Saving %s (as unbound)" ',var)
>            (setq cc-bytecomp-unbound-variables
>                  (cons ',var cc-bytecomp-unbound-variables))))
> -       (if (and (cc-bytecomp-is-compiling)
> -             (not load-in-progress))
> +       (if (cc-bytecomp-is-compiling)
>          (progn
>            (defvar ,var)
>            (set ',var (intern (concat "cc-bytecomp-ignore-var:"
> @@ -344,8 +382,7 @@ at compile time, e.g. for macros and inline functions."
>            (setq cc-bytecomp-original-functions
>                  (cons (list ',fun nil 'unbound)
>                        cc-bytecomp-original-functions))))
> -       (if (and (cc-bytecomp-is-compiling)
> -             (not load-in-progress))
> +       (if (cc-bytecomp-is-compiling)
>          (progn
>            (fset ',fun (intern (concat "cc-bytecomp-ignore-fun:"
>                                        (symbol-name ',fun))))
> diff --git a/lisp/progmodes/cc-defs.el b/lisp/progmodes/cc-defs.el
> index 1d8b8ab..b0e83f3 100644
> --- a/lisp/progmodes/cc-defs.el
> +++ b/lisp/progmodes/cc-defs.el
> @@ -1823,19 +1823,22 @@ system."
> 
> (defvar c-lang-const-expansion nil)
> 
> +;; Ugly hack to pull in the definition of `cc-bytecomp-compiling-or-loading`
> +;; from cc-bytecomp to make it available at loadtime.  This is the same
> +;; mechanism used in cc-mode.el for `c-populate-syntax-table'.
> +(defalias 'cc-bytecomp-compiling-or-loading
> +  (cc-eval-when-compile
> +    (let ((f (symbol-function 'cc-bytecomp-compiling-or-loading)))
> +      (if (byte-code-function-p f) f (byte-compile f)))))
> +
> (defsubst c-get-current-file ()
>   ;; Return the base name of the current file.
> -  (let ((file (cond
> -            (load-in-progress
> -             ;; Being loaded.
> -             load-file-name)
> -            ((and (boundp 'byte-compile-dest-file)
> -                  (stringp byte-compile-dest-file))
> -             ;; Being compiled.
> -             byte-compile-dest-file)
> -            (t
> -             ;; Being evaluated interactively.
> -             (buffer-file-name)))))
> +  (let* ((c-or-l (cc-bytecomp-compiling-or-loading))
> +      (file
> +       (cond
> +        ((eq c-or-l 'loading) load-file-name)
> +        ((eq c-or-l 'compiling) byte-compile-dest-file)
> +        ((null c-or-l) (buffer-file-name)))))
>     (and file
>        (file-name-sans-extension
>         (file-name-nondirectory file)))))
> @@ -2073,9 +2076,7 @@ quoted."
>         (if (or (eq c-lang-const-expansion 'call)
>                 (and (not c-lang-const-expansion)
>                      (not mode))
> -                load-in-progress
> -                (not (boundp 'byte-compile-dest-file))
> -                (not (stringp byte-compile-dest-file)))
> +             (not (cc-bytecomp-is-compiling)))
>             ;; Either a straight call is requested in the context, or
>             ;; we're in an "uncontrolled" context and got no language,
>             ;; or we're not being byte compiled so the compile time
> diff --git a/lisp/progmodes/cc-langs.el b/lisp/progmodes/cc-langs.el
> index 68b2d62..22d78b5 100644
> --- a/lisp/progmodes/cc-langs.el
> +++ b/lisp/progmodes/cc-langs.el
> @@ -3252,10 +3252,7 @@ function it returns is byte compiled with all the 
> evaluated results
> from the language constants.  Use the `c-init-language-vars' macro to
> accomplish that conveniently."
> 
> -  (if (and (not load-in-progress)
> -        (boundp 'byte-compile-dest-file)
> -        (stringp byte-compile-dest-file))
> -
> +  (if (cc-bytecomp-is-compiling)
>       ;; No need to byte compile this lambda since the byte compiler is
>       ;; smart enough to detect the `funcall' construct in the
>       ;; `c-init-language-vars' macro below and compile it all straight
> 
> 
> 
> -- 
> Alan Mackenzie (Nuremberg, Germany).






reply via email to

[Prev in Thread] Current Thread [Next in Thread]