Subject: Re: bug#7722: 24.0.50; Finding this C++ header file drops emacs into a infinite loop

From: Alan Mackenzie <[email-masked]>
Date: Fri, 4 Feb 2011 22:22:07 +0000

Hi there, Yidong, Tassilo,

On Sat, Jan 22, 2011 at 03:37:24PM -0500, Chong Yidong wrote:
> Tassilo Horn <[email-masked]> writes:

> > Originally reported by Caligo <[email-masked]>

> > He uses GNU Emacs 23.2.1 (x86_64-pc-linux-gnu, GTK+ Version 2.20.1),
> > but the bug in still in the current bzr trunk.

> > 1. emacs -Q bug.hpp
> > 2. emacs loops infinitely using 100% CPU resources

> > The offending file is that (according to the original reporter, the
> > spaces and empty lines are needed):

> I can reproduce this (file attached for convenience).  Alan, could you
> take a look?  Looks like a loop in c-forward-<>-arglist-recur:

It was indeed such a loop.  It was caused by a 500n jit-lock boundary
falling in the middle of a template construct, hence the "necessity" of
all the whitespace to reproduce the failure.

Here's a putative patch for the problem.  I've refactored the offending
function by replacing obscenely nested `if'-forms with a simple `cond'.
I've also removed some narrowing (to the 500n limit) which should help
jit-lock, hopefully without hurting too much elsewhere.

Tassilo, would you try out the patch, please, and let me know how it
goes.  Thanks!

=== modified file 'lisp/progmodes/cc-engine.el'
*** lisp/progmodes/cc-engine.el	2011-01-31 23:54:50 +0000
--- lisp/progmodes/cc-engine.el	2011-02-04 22:12:46 +0000
***************
*** 5455,5463 ****
  	    (goto-char start)
  	    nil))
  
!       (forward-char)
  
        (unless (looking-at c-<-op-cont-regexp)
  	(while (and
  		(progn
  		  (c-forward-syntactic-ws)
--- 5455,5465 ----
  	    (goto-char start)
  	    nil))
  
!       (forward-char) ; Forward over the opening '<'.
  
        (unless (looking-at c-<-op-cont-regexp)
+ 	;; go forward one non-alphanumeric character (group) per iteration of
+ 	;; this loop.
  	(while (and
  		(progn
  		  (c-forward-syntactic-ws)
***************
*** 5486,5492 ****
  			    (c-forward-type)
  			    (c-forward-syntactic-ws))))))
  
! 		  (setq pos (point))
  
  		  ;; Note: These regexps exploit the match order in \| so
  		  ;; that "<>" is matched by "<" rather than "[^>:-]>".
--- 5488,5494 ----
  			    (c-forward-type)
  			    (c-forward-syntactic-ws))))))
  
! 		  (setq pos (point))	; e.g. first token inside the '<'
  
  		  ;; Note: These regexps exploit the match order in \| so
  		  ;; that "<>" is matched by "<" rather than "[^>:-]>".
***************
*** 5522,5559 ****
  		  ;; Either an operator starting with '<' or a nested arglist.
  		  (setq pos (point))
  		  (let (id-start id-end subres keyword-match)
! 		    (if (if (looking-at c-<-op-cont-regexp)
! 			    (setq tmp (match-end 0))
! 			  (setq tmp pos)
! 			  (backward-char)
! 			  (not
! 			   (and
! 
! 			    (save-excursion
! 			      ;; There's always an identifier before an angle
! 			      ;; bracket arglist, or a keyword in
! 			      ;; `c-<>-type-kwds' or `c-<>-arglist-kwds'.
! 			      (c-backward-syntactic-ws)
! 			      (setq id-end (point))
! 			      (c-simple-skip-symbol-backward)
! 			      (when (or (setq keyword-match
! 					      (looking-at c-opt-<>-sexp-key))
! 					(not (looking-at c-keywords-regexp)))
! 				(setq id-start (point))))
! 
! 			    (setq subres
! 				  (let ((c-promote-possible-types t)
! 					(c-record-found-types t))
! 				    (c-forward-<>-arglist-recur
! 				     (and keyword-match
! 					  (c-keyword-member
! 					   (c-keyword-sym (match-string 1))
! 					   'c-<>-type-kwds)))))
! 			    )))
! 
! 			;; It was not an angle bracket arglist.
! 			(goto-char tmp)
! 
  		      ;; It was an angle bracket arglist.
  		      (setq c-record-found-types subres)
  
--- 5524,5558 ----
  		  ;; Either an operator starting with '<' or a nested arglist.
  		  (setq pos (point))
  		  (let (id-start id-end subres keyword-match)
! 		    (cond
! 		     ;; The '<' begins a multi-char operator.
! 		     ((looking-at c-<-op-cont-regexp)
! 		      (setq tmp (match-end 0))
! 		      (goto-char (match-end 0)))
! 		     ;; We're at a nested <.....>
! 		     ((progn
! 			(setq tmp pos)
! 			(backward-char)	; to the '<'
! 			(and
! 			 (save-excursion
! 			   ;; There's always an identifier before an angle
! 			   ;; bracket arglist, or a keyword in `c-<>-type-kwds'
! 			   ;; or `c-<>-arglist-kwds'.
! 			   (c-backward-syntactic-ws)
! 			   (setq id-end (point))
! 			   (c-simple-skip-symbol-backward)
! 			   (when (or (setq keyword-match
! 					   (looking-at c-opt-<>-sexp-key))
! 				     (not (looking-at c-keywords-regexp)))
! 			     (setq id-start (point))))
! 			 (setq subres
! 			       (let ((c-promote-possible-types t)
! 				     (c-record-found-types t))
! 				 (c-forward-<>-arglist-recur
! 				  (and keyword-match
! 				       (c-keyword-member
! 					(c-keyword-sym (match-string 1))
! 					'c-<>-type-kwds)))))))
  		      ;; It was an angle bracket arglist.
  		      (setq c-record-found-types subres)
  
***************
*** 5567,5574 ****
  				   (c-forward-syntactic-ws)
  				   (looking-at c-opt-identifier-concat-key)))
  			    (c-record-ref-id (cons id-start id-end))
! 			  (c-record-type-id (cons id-start id-end))))))
! 		  t)
  
  		 ((and (not c-restricted-<>-arglists)
  		       (or (and (eq (char-before) ?&)
--- 5566,5578 ----
  				   (c-forward-syntactic-ws)
  				   (looking-at c-opt-identifier-concat-key)))
  			    (c-record-ref-id (cons id-start id-end))
! 			  (c-record-type-id (cons id-start id-end)))))
! 
! 		     ;; At a "less than" operator.
! 		     (t
! 		      (forward-char)
! 		      )))
! 		  t)			; carry on looping.
  
  		 ((and (not c-restricted-<>-arglists)
  		       (or (and (eq (char-before) ?&)

=== modified file 'lisp/progmodes/cc-fonts.el'
*** lisp/progmodes/cc-fonts.el	2011-01-25 04:08:28 +0000
--- lisp/progmodes/cc-fonts.el	2011-02-04 22:10:01 +0000
***************
*** 1082,1088 ****
  	     (boundp 'parse-sexp-lookup-properties))))
  
        ;; Below we fontify a whole declaration even when it crosses the limit,
!       ;; to avoid gaps when lazy-lock fontifies the file a screenful at a
        ;; time.  That is however annoying during editing, e.g. the following is
        ;; a common situation while the first line is being written:
        ;;
--- 1082,1088 ----
  	     (boundp 'parse-sexp-lookup-properties))))
  
        ;; Below we fontify a whole declaration even when it crosses the limit,
!       ;; to avoid gaps when jit/lazy-lock fontifies the file a block at a
        ;; time.  That is however annoying during editing, e.g. the following is
        ;; a common situation while the first line is being written:
        ;;
***************
*** 1094,1102 ****
        ;; "some_other_variable" as an identifier, and the latter will not
        ;; correct itself until the second line is changed.  To avoid that we
        ;; narrow to the limit if the region to fontify is a single line.
!       (narrow-to-region
!        (point-min)
!        (if (<= limit (c-point 'bonl))
  	   (save-excursion
  	     ;; Narrow after any operator chars following the limit though,
  	     ;; since those characters can be useful in recognizing a
--- 1094,1102 ----
        ;; "some_other_variable" as an identifier, and the latter will not
        ;; correct itself until the second line is changed.  To avoid that we
        ;; narrow to the limit if the region to fontify is a single line.
!       (if (<= limit (c-point 'bonl))
! 	  (narrow-to-region
! 	   (point-min)
  	   (save-excursion
  	     ;; Narrow after any operator chars following the limit though,
  	     ;; since those characters can be useful in recognizing a
***************
*** 1104,1111 ****
  	     ;; after the header).
  	     (goto-char limit)
  	     (skip-chars-forward c-nonsymbol-chars)
! 	     (point))
! 	 limit))
  
        (c-find-decl-spots
         limit
--- 1104,1110 ----
  	     ;; after the header).
  	     (goto-char limit)
  	     (skip-chars-forward c-nonsymbol-chars)
! 	     (point))))
  
        (c-find-decl-spots
         limit

-- 
Alan Mackenzie (Nuremberg, Germany).