unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
* bug#37910: CC Mode 5.33.2 (C++//l); CC-mode inconsistently indents everything as topmost-intro after a while
       [not found] <CANJXwL=WHfvFSAHxxNQ=92_R193gFPU7Hu7zwXCCEO3Z0Nyn3A@mail.gmail.com>
@ 2019-10-27 15:39 ` Alan Mackenzie
  2019-11-10 10:48   ` Alan Mackenzie
  0 siblings, 1 reply; 12+ messages in thread
From: Alan Mackenzie @ 2019-10-27 15:39 UTC (permalink / raw)
  To: Eric Scrivner; +Cc: 37910

Hello, Eric.

On Thu, Oct 24, 2019 at 12:06:18 -0700, Eric Scrivner wrote:
> This seems related to (if not the same as) bug #5490.

> - This happens randomly and then randomly stops happening (cache expiry
> maybe?)
> - M-x revert-buffer does not fix.

Thanks for taking the trouble to report this bug, and thanks even more
for including so much information.

> Here's a snippet from the buffer at the time this happen, as you can see
> there seems to be a region until the end where everything becomes
> topmost-intro:

>       }                          // ((block-close 18328))
>                                 // ((statement 9560))
>       SDL_DestroyWindow(Window); // ((statement 9560))
>     }    // ((block-close 9490))
>     else                        // ((else-clause 9466))
>     {                                                             //
> ((substatement-open 18464))
>       PlatformLog("Failed to create window: %s", SDL_GetError()); //
> ((statement-block-intro 18473))
>     } // ((block-close 18473))
>                                 // ((topmost-intro 18576))
>                                 // ((topmost-intro 18576))
> SDL_Quit();                     // ((topmost-intro 18541))
>   }                             // ((topmost-intro 18548))
>   else                          // ((else-clause 9188))
>   {                             // ((substatement-open 18845))
>     PlatformLog("Failed to initialize SDL: %s", SDL_GetError()); //
> ((statement-block-intro 18901))((statement-block-intro 18724))
>   }                                                              //
> ((block-close 18901))
>                                 // ((topmost-intro 19093))
>   return EXIT_SUCCESS;          // ((topmost-intro 19093))
> }                               // ((topmost-intro 19242))

At the moment, all I can do is acknowledge receipt of your report.  It's
obviously not an easy bug to diagnose.

I can see two ways of making progress: (i) Inspecting
c-guess-basic-syntax, the function which analyses code and produces
(amongs other things) all these topmost-intro's.  It is essentially a
large cond form (Lisp's equivalent of a switch), and the single place
which produces topmost-intro comes fairly early on in this cond form;
(ii) Determine what aspects of a buffer do not get reinitialised after
evaluating M-x revert-buffer.  That could provide some clue.

[ CC Mode config dump acknowledged with thanks, but snipped ]

Just one thing.  If you haven't already done so, could you make a backup
copy of a buffer which triggers the bug, just in case after some future
edit it no longer does so.  Thanks!

-- 
Alan Mackenzie (Nuremberg, Germany).





^ permalink raw reply	[flat|nested] 12+ messages in thread

* bug#37910: CC Mode 5.33.2 (C++//l); CC-mode inconsistently indents everything as topmost-intro after a while
  2019-10-27 15:39 ` bug#37910: CC Mode 5.33.2 (C++//l); CC-mode inconsistently indents everything as topmost-intro after a while Alan Mackenzie
@ 2019-11-10 10:48   ` Alan Mackenzie
  2019-11-10 17:47     ` Eric Scrivner
  0 siblings, 1 reply; 12+ messages in thread
From: Alan Mackenzie @ 2019-11-10 10:48 UTC (permalink / raw)
  To: Eric Scrivner; +Cc: 37910

Hello, Eric.

Now for a top-post.  ;-)

I think I may have found the cause of the bug.  It may have been caused
by overwriting lisp list structure, rather than creating new (parallel)
list structures - a sort of corruption.  See below for a fuller
explanation.  This hypothesis is entirely consistent with the observed
result (spurious topmost-intro's).

Would you please apply the patch below and byte-compile the result.  It
suffices just to compile cc-engine.el.  (If you want any help with
applying the patch or byte compiling, feel free to send me private
email.)

Then please try out the patched CC Mode for however long it has taken,
in the past, to see the sort of bug you reported, and then somewhat
longer.  If the bug fails to show itself, we may well have fixed it.
Please let me know how things are going.

############## Optional section.  An explanation #######################

One of the caches CC Mode uses, "c-state-cache", keeps track of the
nested brace structure.  It is (re)calculated on calling the lisp
function c-parse-state.  In essence, it records the position of each
enclosing brace in a list, the most nested first.  So if we had the
following C++ structure:

    { // 1
    ......
        { // 2
	......
	    { // 3
	    ...... <point>

, and <point> was at the marked position, our c-state-cache would be the
list of the three brace positions (P3 P2 P1).  One of its uses is
determining if some point is at the top level or not.  If the
c-state-cache list for that point is empty, it is at the top level.

As point moves through the buffer, and c-parse-state is called from
somewhere else, c-state-cache is "altered" in a highly optimised
fashion.  This avoids having to scan large portions of the buffer too
often, to determine the brace structure.  The nature of this "altering"
is what is causing the problem.

When the buffer is narrowed, say beginning with the brace 2, calling
c-parse-state now has to return (P3 P2), because the brace 1 is now
outside the visible portion.

The suspected bug cause is the way (P3 P2 P1) is changed to (P3 P2) on
this narrowing.  Up to now the list structure itself has been changed,
rather than making a copy of the structure.

So, what may have been happening is that CC Mode is looping through the
c-state-cache to determine whether point is at top level.  (Being
directly inside a class or namespace, etc., counts as "top level").  If
point is inside brace 1, the loop will try to determine that P1 is not a
class, etc., and return "not at top level".  However, if c-parse-state
had been called with the above narrowing, c-state-cache is now (P3 P2),
point appears to be outside every brace, and the loop spuriously returns
"at top level".  This is what I think has been happening.

When the code wrongly reports "at top level", we get the unwanted
topmost-intro analyses.

The solution to this is when the buffer is narrowed and we call
c-parse-state, we make a COPY of the c-state-cache list, leaving the
original unmolested for its original owner.  This is what the patch
does.

############## End of optional section. ################################

Here is the patch.  It should work in Emacs-26.3, even though the line
numbers are now a bit different:



diff -r 2783baa48d44 cc-engine.el
--- a/cc-engine.el	Fri Oct 25 20:00:14 2019 +0000
+++ b/cc-engine.el	Sun Nov 10 10:30:17 2019 +0000
@@ -3690,7 +3690,13 @@
 							; brace pair.
 	    (setq c-state-cache nil
 		  c-state-cache-good-pos c-state-min-scan-pos)
-	  (setcdr ptr nil)
+	  ;; Do not alter the original `c-state-cache' structure, since there
+	  ;; may be a loop suspended which is looping through that structure.
+	  ;; This may have been the cause of bug #37910.
+	  (let ((cdr-ptr (cdr ptr)))
+	    (setcdr ptr nil)
+	    (setq c-state-cache (copy-sequence c-state-cache))
+	    (setcdr ptr cdr-ptr))
 	  (setq c-state-cache-good-pos (1+ (c-state-cache-top-lparen))))
 	)))
 
@@ -3793,11 +3799,12 @@
 		(setq new-cons (cons bra (1+ ce)))
 		(cond
 		 ((consp (car c-state-cache))
-		  (setcar c-state-cache new-cons))
+		  (setq c-state-cache (cons new-cons (cdr c-state-cache))))
 		 ((and (numberp (car c-state-cache)) ; probably never happens
 		       (< ce (car c-state-cache)))
-		  (setcdr c-state-cache
-			  (cons new-cons (cdr c-state-cache))))
+		  (setq c-state-cache
+			(cons (car c-state-cache)
+			      (cons new-cons (cdr c-state-cache)))))
 		 (t (setq c-state-cache (cons new-cons c-state-cache)))))
 
 	    ;; We haven't found a brace pair.  Record this in the cache.
@@ -3998,7 +4005,7 @@
 	(when (and c-state-cache
 		   (consp (car c-state-cache))
 		   (> (cdar c-state-cache) upper-lim))
-	  (setcar c-state-cache (caar c-state-cache))
+	  (setq c-state-cache (cons (caar c-state-cache) (cdr c-state-cache)))
 	  (setq scan-back-pos (car c-state-cache)
 		cons-separated t))
 
@@ -4135,7 +4142,7 @@
       ;; knowledge of what's inside these braces, we have no alternative but
       ;; to direct the caller to scan the buffer from the opening brace.
       (setq pos (caar c-state-cache))
-      (setcar c-state-cache pos)
+      (setq c-state-cache (cons pos (cdr c-state-cache)))
       (list (1+ pos) pos t)) ; return value.  We've just converted a brace pair
 			     ; entry into a { entry, so the caller needs to
 			     ; search for a brace pair before the {.




On Sun, Oct 27, 2019 at 15:39:56 +0000, Alan Mackenzie wrote:
> Hello, Eric.

> On Thu, Oct 24, 2019 at 12:06:18 -0700, Eric Scrivner wrote:
> > This seems related to (if not the same as) bug #5490.

> > - This happens randomly and then randomly stops happening (cache expiry
> > maybe?)
> > - M-x revert-buffer does not fix.

> Thanks for taking the trouble to report this bug, and thanks even more
> for including so much information.

> > Here's a snippet from the buffer at the time this happen, as you can see
> > there seems to be a region until the end where everything becomes
> > topmost-intro:

> >       }                          // ((block-close 18328))
> >                                 // ((statement 9560))
> >       SDL_DestroyWindow(Window); // ((statement 9560))
> >     }    // ((block-close 9490))
> >     else                        // ((else-clause 9466))
> >     {                                                             //
> > ((substatement-open 18464))
> >       PlatformLog("Failed to create window: %s", SDL_GetError()); //
> > ((statement-block-intro 18473))
> >     } // ((block-close 18473))
> >                                 // ((topmost-intro 18576))
> >                                 // ((topmost-intro 18576))
> > SDL_Quit();                     // ((topmost-intro 18541))
> >   }                             // ((topmost-intro 18548))
> >   else                          // ((else-clause 9188))
> >   {                             // ((substatement-open 18845))
> >     PlatformLog("Failed to initialize SDL: %s", SDL_GetError()); //
> > ((statement-block-intro 18901))((statement-block-intro 18724))
> >   }                                                              //
> > ((block-close 18901))
> >                                 // ((topmost-intro 19093))
> >   return EXIT_SUCCESS;          // ((topmost-intro 19093))
> > }                               // ((topmost-intro 19242))

> At the moment, all I can do is acknowledge receipt of your report.  It's
> obviously not an easy bug to diagnose.

> I can see two ways of making progress: (i) Inspecting
> c-guess-basic-syntax, the function which analyses code and produces
> (amongs other things) all these topmost-intro's.  It is essentially a
> large cond form (Lisp's equivalent of a switch), and the single place
> which produces topmost-intro comes fairly early on in this cond form;
> (ii) Determine what aspects of a buffer do not get reinitialised after
> evaluating M-x revert-buffer.  That could provide some clue.

> [ CC Mode config dump acknowledged with thanks, but snipped ]

> Just one thing.  If you haven't already done so, could you make a backup
> copy of a buffer which triggers the bug, just in case after some future
> edit it no longer does so.  Thanks!

-- 
Alan Mackenzie (Nuremberg, Germany).





^ permalink raw reply	[flat|nested] 12+ messages in thread

* bug#37910: CC Mode 5.33.2 (C++//l); CC-mode inconsistently indents everything as topmost-intro after a while
  2019-11-10 10:48   ` Alan Mackenzie
@ 2019-11-10 17:47     ` Eric Scrivner
  2019-11-10 17:50       ` Eric Scrivner
                         ` (2 more replies)
  0 siblings, 3 replies; 12+ messages in thread
From: Eric Scrivner @ 2019-11-10 17:47 UTC (permalink / raw)
  To: Alan Mackenzie; +Cc: 37910

[-- Attachment #1: Type: text/plain, Size: 29851 bytes --]

I've applied the patch and run emacs with it. Unfortunately, it doesn't
seem to solve the issue. One the bright side, I believe I had buffers which
can reliably reproduce the issue (at least locally). I've copied one such
file below. The issue can be reproduced by simply going to the end of
`main` and attempting to indent the `SQL_Quit()` statement. At first it
attempts to overindent the line as `statement-cont`, then if I go up and
attempt to indent the `PlatformLog` in the else statement then return to
the SDL_Quit and indent it, it has become `topmost-intro`. Apologies for
the long length of the file, but hopefully it can help with reproduction:

#include "engine_math.h"
#include "engine_types.h"
#include "platform.h"

#include <SDL.h>
#include <SDL_opengl.h>

#include <sys/mman.h>
#include <x86intrin.h>

const f32 VIEWPORT_WIDTH = 1920;
const f32 VIEWPORT_HEIGHT = 1080;

///////////////////////////////////////////////////////////////////////////////

typedef struct {
  game_input Input;
} sdl2_input_state;

typedef struct {
  i32 ToneHz;
  i32 ToneVolume;
  i32 SampleIndex;
  i32 SamplesPerSecond;
  i32 BytesPerSample;
  i32 WavePeriod;
  f64 SineLocation;
} sdl2_audio_config;

typedef struct {
  u8* Buffer;
  i32 Size;
  i32 ReadCursor;
  i32 WriteCursor;
  b32 IsPlaying;
  SDL_AudioDeviceID DeviceID;
  sdl2_audio_config* AudioConfig;
} sdl2_audio_buffer;

#if HYPERBOREAN_DEBUG
typedef struct {
  v2 Pos;
  v3 Color;
} sdl2_debug_colored_vertex;

typedef struct {
  i32 PlayCursor;
  i32 WriteCursor;
  sdl2_debug_colored_vertex V[2];
} sdl2_debug_time_marker;
#endif

///////////////////////////////////////////////////////////////////////////////

global_variable b32 GlobalRunning;
global_variable sdl2_input_state GlobalGameInput;
global_variable sdl2_audio_buffer GlobalAudioBuffer;

///////////////////////////////////////////////////////////////////////////////

void PlatformLog(const char* const Format, ...)
{
  va_list args;
  va_start(args, Format);

  SDL_LogMessageV(
    SDL_LOG_CATEGORY_APPLICATION,
    SDL_LOG_PRIORITY_INFO,
    Format,
    args
  );

  va_end(args);
}

b32 PlatformReadEntireFile(const char* path, platform_entire_file* Result) {
  FILE* file = fopen(path, "r");

  if (file == NULL) {
    return false;
  }

  fseek(file, 0, SEEK_END);
  Result->Size = ftell(file);
  fseek(file, 0, SEEK_SET);

  Result->Contents = (u8*)malloc(Result->Size);
  fread(Result->Contents, Result->Size, 1, file);
  fclose(file);

  return true;
}

void PlatformFreeEntireFile(platform_entire_file* Result) {
  if (Result->Contents) {
    free(Result->Contents);
    Result->Contents = NULL;
    Result->Size = 0;
  }
}

void PlatformAudioLock() {
  SDL_LockAudioDevice(GlobalAudioBuffer.DeviceID);
}

void PlatformAudioUnlock() {
  SDL_UnlockAudioDevice(GlobalAudioBuffer.DeviceID);
}

///////////////////////////////////////////////////////////////////////////////

internal void *SDL2VirtualAlloc(u64 SizeBytes, void *BaseAddress = NULL) {
  return mmap(BaseAddress, SizeBytes, PROT_READ | PROT_WRITE,
              MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
}

///////////////////////////////////////////////////////////////////////////////

internal void SDL2VirtualFree(void *Buffer, u64 SizeBytes) {
  munmap(Buffer, SizeBytes);
}

///////////////////////////////////////////////////////////////////////////////

internal bool SDL2VirtualAllocSucceeded(void *Value) {
  return Value != MAP_FAILED;
}

///////////////////////////////////////////////////////////////////////////////

internal void SDL2FillAudioDeviceBuffer(void *UserData, u8 *DeviceBuffer,
                                        i32 Length) {
  Assert(UserData != NULL);
  Assert(DeviceBuffer != NULL);

  SDL_memset(DeviceBuffer, 0, Length);

  sdl2_audio_buffer *AudioBuffer = (sdl2_audio_buffer *)UserData;

  // Keep track of two regions. Region1 contains everything from the current
  // ReadCursor up until, potentially, the end of the buffer. Region2 only
  // exists if we need to circle back around. It contains all the data from
the
  // beginning of the buffer up until sufficient bytes are read to meet
Length.
  int Region1Size = Length;
  int Region2Size = 0;
  if (AudioBuffer->ReadCursor + Length > AudioBuffer->Size) {
    // Handle looping back from the beginning.
    Region1Size = AudioBuffer->Size - AudioBuffer->ReadCursor;
    Region2Size = Length - Region1Size;
  }

  SDL_memcpy(DeviceBuffer, (AudioBuffer->Buffer + AudioBuffer->ReadCursor),
             Region1Size);
  SDL_memcpy(&DeviceBuffer[Region1Size], AudioBuffer->Buffer, Region2Size);

  AudioBuffer->ReadCursor =
      (AudioBuffer->ReadCursor + Length) % AudioBuffer->Size;
}

///////////////////////////////////////////////////////////////////////////////

internal void SDL2InitializeAudio(sdl2_audio_buffer *AudioBuffer) {
  AudioBuffer->Size = AudioBuffer->AudioConfig->SamplesPerSecond *
                      AudioBuffer->AudioConfig->BytesPerSample;
  AudioBuffer->WriteCursor = AudioBuffer->AudioConfig->BytesPerSample;
  AudioBuffer->ReadCursor = 0;
  AudioBuffer->Buffer = (u8 *)SDL2VirtualAlloc(AudioBuffer->Size);
  Assert(SDL2VirtualAllocSucceeded(AudioBuffer->Buffer));

  SDL_AudioSpec AudioSettings = {};
  AudioSettings.freq = AudioBuffer->AudioConfig->SamplesPerSecond;
  AudioSettings.format = AUDIO_S16;
  AudioSettings.channels = 2;
  // NOTE: Reduce the number of samples here to decrease audio lag by
causing
  // SDL to request audio more frequently.
  AudioSettings.samples = 256;
  AudioSettings.callback = &SDL2FillAudioDeviceBuffer;
  AudioSettings.userdata = (void *)AudioBuffer;

  SDL_AudioSpec ObtainedSettings = {};
  AudioBuffer->DeviceID =
      SDL_OpenAudioDevice(NULL, 0, &AudioSettings, &ObtainedSettings, 0);

  if (AudioSettings.format != ObtainedSettings.format) {
    SDL_Log("Could not open audio device: %s", SDL_GetError());
    exit(1);
  }
}

///////////////////////////////////////////////////////////////////////////////

internal void SDL2Cleanup(sdl2_audio_buffer *AudioBuffer,
                          sdl2_input_state *InputState) {
  if (AudioBuffer->Buffer) {
    SDL2VirtualFree(AudioBuffer->Buffer, AudioBuffer->Size);
  }

  SDL_CloseAudioDevice(AudioBuffer->DeviceID);
}

///////////////////////////////////////////////////////////////////////////////

internal void SDL2ProcessKeyDown(game_button_state *State, b32 IsDown) {
  Assert(IsDown != State->EndedDown);
  State->EndedDown = IsDown;
  ++State->HalfTransitionCount;
}

///////////////////////////////////////////////////////////////////////////////

#if HYPERBOREAN_DEBUG
internal u32 SDL2DebugVAO;
internal u32 SDL2DebugVBO;

internal void SDL2DrawSoundBufferMarker(sdl2_audio_buffer *AudioBuffer, f32
C,
                                        f32 PadX, f32 Top, f32 Bottom,
                                        i32 Value,
                                        sdl2_debug_colored_vertex *Vertices,
                                        v3 Color, u32 Shader) {
  Assert(Value < AudioBuffer->Size);
  f32 XReal = C * Value + PadX;
  Vertices[0].Pos = {{XReal, Top}};
  Vertices[0].Color = Color;
  Vertices[1].Pos = {{XReal, Bottom}};
  Vertices[1].Color = Color;
  glUseProgram(Shader);
  m4x4 Projection =
      OrthographicProjection(0, VIEWPORT_WIDTH, 0, VIEWPORT_HEIGHT, -1.0,
1.0);
  GLint ProjectionLoc = glGetUniformLocation(Shader, "Projection");
  glUniformMatrix4fv(ProjectionLoc, 1, GL_FALSE, (float *)Projection.E);
  glBindBuffer(GL_ARRAY_BUFFER, SDL2DebugVBO);
  glBufferSubData(GL_ARRAY_BUFFER, 0, 2 * sizeof(sdl2_debug_colored_vertex),
                  Vertices);
  glBindVertexArray(SDL2DebugVAO);
  glDrawArrays(GL_LINES, 0, 2);
  glBindVertexArray(0);
}

internal void SDL2DebugSyncDisplay(sdl2_audio_buffer *AudioBuffer,
                                   u32 MarkerCount,
                                   sdl2_debug_time_marker *Markers,
                                   f32 TargetSecondsPerFrame, u32 Shader) {
  f32 PadX = 16;
  f32 PadY = 16;

  f32 Top = PadY;
  f32 Bottom = VIEWPORT_HEIGHT - PadY;

  f32 C = (f32)(VIEWPORT_WIDTH - 2 * PadX) / (f32)AudioBuffer->Size;
  for (u32 MarkerIndex = 0; MarkerIndex < MarkerCount; ++MarkerIndex) {
    sdl2_debug_time_marker *Marker = &Markers[MarkerIndex];

    SDL2DrawSoundBufferMarker(AudioBuffer, C, PadX, Top, Bottom,
                              Marker->PlayCursor, Marker->V, {{1.0, 1.0,
1.0}},
                              Shader);
    SDL2DrawSoundBufferMarker(AudioBuffer, C, PadX, Top, Bottom,
                              Marker->WriteCursor, Marker->V, {{1.0, 1.0,
0.0}},
                              Shader);
  }
}
#endif

///////////////////////////////////////////////////////////////////////////////

internal int
SDL2GetWindowRefreshRate(SDL_Window *Window)
{
    SDL_DisplayMode Mode;
    int DisplayIndex = SDL_GetWindowDisplayIndex(Window);
    // If we can't find the refresh rate, we'll return this:
    int DefaultRefreshRate = 60;
    if (SDL_GetDesktopDisplayMode(DisplayIndex, &Mode) != 0)
    {
        return DefaultRefreshRate;
    }
    if (Mode.refresh_rate == 0)
    {
        return DefaultRefreshRate;
    }
    return Mode.refresh_rate;
}

///////////////////////////////////////////////////////////////////////////////

internal void SDL2HandleEvent(SDL_Event* Event, sdl2_input_state*
InputState)
{
  if (Event->type == SDL_QUIT)
  {
    GlobalRunning = false;
  }
  else if (Event->type == SDL_KEYDOWN || Event->type == SDL_KEYUP)
  {
    SDL_Keycode KeyCode = Event->key.keysym.sym;
    bool IsDown = Event->key.state == SDL_PRESSED;
    bool WasDown = (Event->key.state == SDL_RELEASED || Event->key.repeat
!= 0);

    // Eat key repeats
    if (IsDown != WasDown)
    {
      if (KeyCode == SDLK_LEFT)
      {
        SDL2ProcessKeyDown(&InputState->Input.Controller.MoveLeft, IsDown);
      }
      else if (KeyCode == SDLK_RIGHT)
      {
        SDL2ProcessKeyDown(&InputState->Input.Controller.MoveRight, IsDown);
      }
      else if (KeyCode == SDLK_UP)
      {
        SDL2ProcessKeyDown(&InputState->Input.Controller.MoveUp, IsDown);
      }
      else if (KeyCode == SDLK_DOWN)
      {
        SDL2ProcessKeyDown(&InputState->Input.Controller.MoveDown, IsDown);
      }
      else if (KeyCode == SDLK_SPACE)
      {
        SDL2ProcessKeyDown(&InputState->Input.Controller.ActionDown,
IsDown);
      }
      else if (KeyCode == SDLK_ESCAPE)
      {
        SDL2ProcessKeyDown(&InputState->Input.Controller.Back, IsDown);
      }
    }
  }
  else if (Event->type == SDL_MOUSEMOTION)
  {
    InputState->Input.MouseP.X = Event->motion.x;
    InputState->Input.MouseP.Y = Event->motion.y;
  }
  else if (Event->type == SDL_MOUSEBUTTONDOWN || Event->type ==
SDL_MOUSEBUTTONUP)
  {
    bool IsDown = Event->button.state == SDL_PRESSED;
    bool WasDown = (Event->button.state == SDL_RELEASED ||
Event->button.clicks != 1);

    if (IsDown != WasDown) {
      if (Event->button.button == SDL_BUTTON_LEFT) {
        SDL2ProcessKeyDown(&InputState->Input.MouseLeft, IsDown);
      }
      if (Event->button.button == SDL_BUTTON_RIGHT) {
        SDL2ProcessKeyDown(&InputState->Input.MouseRight, IsDown);
      }
    }
  }
}

///////////////////////////////////////////////////////////////////////////////

int main() {
  if (SDL_Init(SDL_INIT_EVERYTHING) == 0)
  {
    SDL_Window* Window = SDL_CreateWindow(
      "Hyperborean",
      SDL_WINDOWPOS_UNDEFINED,
      SDL_WINDOWPOS_UNDEFINED,
      VIEWPORT_WIDTH,
      VIEWPORT_HEIGHT,
      SDL_WINDOW_OPENGL | SDL_WINDOW_ALLOW_HIGHDPI
    );

    if (Window != NULL)
    {
      SDL_GLContext GLContext = SDL_GL_CreateContext(Window);

      if (GLContext != NULL)
      {
        GLenum GlewResult = glewInit();
        if (GlewResult == GLEW_OK)
        {
          PlatformLog("OpenGL Vendor: %s", glGetString(GL_VENDOR));
          PlatformLog("OpenGL Version: %s", glGetString(GL_VERSION));
          PlatformLog("OpenGL Renderer: %s", glGetString(GL_RENDERER));

          if (SDL_GL_SetSwapInterval(1) != 0) {
            PlatformLog("Unable to SetSwapInterval: %s", SDL_GetError());
          }

          game_memory GameMemory = {};

#if HYPERBOREAN_INTERNAL
          // NOTE: Hard-code the base address so locations remain constant
between runs
          void* BaseAddress = (void*)Terabytes(2);
#else
          void* BaseAddress = NULL;
#endif

          GameMemory.PermanentStorageSize = Megabytes(512);
          GameMemory.TransientStorageSize = Gigabytes((u64)4);

          u64 TotalSize =
            GameMemory.PermanentStorageSize +
GameMemory.TransientStorageSize;
          GameMemory.PermanentStorage = SDL2VirtualAlloc(TotalSize,
BaseAddress);
          Assert(SDL2VirtualAllocSucceeded(GameMemory.PermanentStorage));
          GameMemory.TransientStorage = (
            (u8*)GameMemory.PermanentStorage +
GameMemory.PermanentStorageSize);

          // Initialize keyboard input
          GlobalGameInput = {};
          GlobalGameInput.Input.WindowDim.W = VIEWPORT_WIDTH;
          GlobalGameInput.Input.WindowDim.H = VIEWPORT_HEIGHT;
          GlobalGameInput.Input.Controller.IsConnected = true;
          GlobalGameInput.Input.Controller.IsAnalog = true;

          u32 MaxVolume = 3000;

          GlobalAudioBuffer = {};
          sdl2_audio_config AudioConfig = {};
          AudioConfig.ToneHz = 261;
          AudioConfig.ToneVolume = 0.10 * MaxVolume;
          AudioConfig.SampleIndex = 0;
          AudioConfig.SamplesPerSecond = 48000;
          AudioConfig.BytesPerSample = 2 * sizeof(i16);
          AudioConfig.WavePeriod = AudioConfig.SamplesPerSecond /
AudioConfig.ToneHz;
          GlobalAudioBuffer.AudioConfig = &AudioConfig;

          SDL2InitializeAudio(&GlobalAudioBuffer);

          // Get screen refresh rate.
          PlatformLog("Refresh Rate: %d Hz\n",
SDL2GetWindowRefreshRate(Window));
          i32 GameUpdateHz = 60;
          f32 TargetSecondsPerFrame = 1.0f / (float)GameUpdateHz;
          PlatformLog("Target Secs/Frame: %f\n", TargetSecondsPerFrame);

          // TODO(eric): Feed DPI information into our game and use it for
scaling.
          f32 DDPI = 0;
          f32 HDPI = 0;
          f32 VDPI = 0;
          if (SDL_GetDisplayDPI(0, &DDPI, &HDPI, &VDPI) != 0) {
            PlatformLog("Unable to get display DPI: %s", SDL_GetError());
          }
          PlatformLog("Display DPI: %f - %f x %f", DDPI, HDPI, VDPI);

          f32 FPS = 0;
          f32 MPF = 0;

          char WindowTitle[256] = {};
          u64 BeginCounter = SDL_GetPerformanceCounter();
          u64 PerfCounterFrequency = SDL_GetPerformanceFrequency();
          u64 BeginCycles = __rdtsc();

#if  HYPERBOREAN_DEBUG
          game_state* State = (game_state*)GameMemory.PermanentStorage;
          u32 DebugMarkerShader = CompileShaders(
            &State->TransientArena,
            R"END(
#version 430 core
layout (location = 0) in vec2 Position;
layout (location = 1) in vec3 Color;
uniform mat4 Projection;

out vec3 FragmentColor;

void main()
{
  FragmentColor = Color;
  gl_Position = vec4(Position, 0.0, 1.0) * Projection;
}
)END",
            R"END(
#version 430 core
in vec3 FragmentColor;

out vec4 ResultingColor;

void main()
{
  ResultingColor = vec4(FragmentColor, 1.0);
}
)END"
          );
          u32 DebugTimeMarkerIndex = 0;
          sdl2_debug_time_marker DebugTimeMarkers[GameUpdateHz / 2] = {0};
          PlatformLog("Num Markers: %d\n", ArrayCount(DebugTimeMarkers));

          glGenVertexArrays(1, &SDL2DebugVAO);
          glBindVertexArray(SDL2DebugVAO);

          glGenBuffers(1, &SDL2DebugVBO);
          glBindBuffer(GL_ARRAY_BUFFER, SDL2DebugVBO);
          glBufferData(GL_ARRAY_BUFFER,
2*sizeof(sdl2_debug_colored_vertex), NULL, GL_DYNAMIC_DRAW);
          glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE,
sizeof(sdl2_debug_colored_vertex), NULL);
          glEnableVertexAttribArray(0);
          glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE,
sizeof(sdl2_debug_colored_vertex), (void*)sizeof(v2));
          glEnableVertexAttribArray(1);

          glBindBuffer(GL_ARRAY_BUFFER, 0);
          glBindVertexArray(0);
#endif

          u32 BeginTimeMs = SDL_GetTicks();

          GlobalRunning = true;
          SDL_Event event;
          while (GlobalRunning) {
            while (SDL_PollEvent(&event)) {
              SDL2HandleEvent(&event, &GlobalGameInput);
            }

            game_input GameInput = {};
            memcpy(&GameInput, &GlobalGameInput.Input, sizeof(game_input));

            game_audio_buffer AudioBuffer = {};
            AudioBuffer.Buffer = GlobalAudioBuffer.Buffer;
            AudioBuffer.Size = GlobalAudioBuffer.Size;
            AudioBuffer.ReadCursor = GlobalAudioBuffer.ReadCursor;
            AudioBuffer.WriteCursor = GlobalAudioBuffer.WriteCursor;
            AudioBuffer.ToneVolume =
GlobalAudioBuffer.AudioConfig->ToneVolume;
            AudioBuffer.SamplesPerSecond =
              GlobalAudioBuffer.AudioConfig->SamplesPerSecond;
            AudioBuffer.BytesPerSample =
GlobalAudioBuffer.AudioConfig->BytesPerSample;

            SDL_LockAudioDevice(GlobalAudioBuffer.DeviceID);
            u32 EndTimeMs = SDL_GetTicks();
            GameInput.DeltaTimeSecs = (EndTimeMs - BeginTimeMs) / 1000.0f;
            GameUpdateAndRender(&GameMemory, &GameInput, &AudioBuffer);
            BeginTimeMs = EndTimeMs;
            SDL_UnlockAudioDevice(GlobalAudioBuffer.DeviceID);

            // NOTE: Unpause for the first time here to avoid startup lag
due
            // to SDL already playing audio from an empty buffer. Unpausing
            // here allows audio to begin playing immediately at application
            // start.
            if (!GlobalAudioBuffer.IsPlaying)
            {
              SDL_PauseAudioDevice(GlobalAudioBuffer.DeviceID, 0);
              GlobalAudioBuffer.IsPlaying = true;
            }

            // NOTE: Copy back out the write cursor state for the circular
audio buffer
            GlobalAudioBuffer.WriteCursor = AudioBuffer.WriteCursor;

            for (size_t ButtonIndex = 0;
                 ButtonIndex <
ArrayCount(GlobalGameInput.Input.Controller.Buttons);
                 ButtonIndex++)
            {

GlobalGameInput.Input.Controller.Buttons[ButtonIndex].HalfTransitionCount =
0;
            }

            if (GameInput.QuitRequested)
            {
              GlobalRunning = false;
            }

#if HYPERBOREAN_DEBUG
            {
              sdl2_debug_time_marker* Marker =
&DebugTimeMarkers[DebugTimeMarkerIndex++];
              if (DebugTimeMarkerIndex >= ArrayCount(DebugTimeMarkers)) {
                DebugTimeMarkerIndex = 0;
              }

              Marker->PlayCursor = GlobalAudioBuffer.ReadCursor;
              Marker->WriteCursor = GlobalAudioBuffer.WriteCursor;

              SDL2DebugSyncDisplay(&GlobalAudioBuffer,
ArrayCount(DebugTimeMarkers), DebugTimeMarkers, TargetSecondsPerFrame,
DebugMarkerShader);
            }
#endif

            SDL_GL_SwapWindow(Window);

            u64 EndCounter = SDL_GetPerformanceCounter();
            u64 CounterElapsed = EndCounter - BeginCounter;

            u64 EndCycles = __rdtsc();
            u64 CyclesElapsed = EndCycles - BeginCycles;
            FPS = PerfCounterFrequency / (float)CounterElapsed;
            f32 SPF = (float)CounterElapsed / PerfCounterFrequency;
            MPF = 1000.0f * CounterElapsed / (double)PerfCounterFrequency;
            i32 MCPF = SafeTruncateUInt64(CyclesElapsed / (1000 * 1000));

            BeginCounter = EndCounter;
            BeginCycles = EndCycles;
            sprintf(
              WindowTitle,
              "Hyperborean - %0.02f f/s, %0.04f s/f, %0.02f ms/f, %d mc/f",
              FPS, SPF, MPF, MCPF
            );
            SDL_SetWindowTitle(Window, WindowTitle);
          }

          GameCleanup(&GameMemory);
#if HYPERBOREAN_DEBUG
          glDeleteBuffers(1, &SDL2DebugVAO);
          glDeleteBuffers(1, &SDL2DebugVBO);
          glDeleteShader(DebugMarkerShader);
#endif

          SDL2VirtualFree(GameMemory.PermanentStorage,
GameMemory.PermanentStorageSize);
          SDL2VirtualFree(GameMemory.TransientStorage,
GameMemory.TransientStorageSize);

          SDL2Cleanup(&GlobalAudioBuffer, &GlobalGameInput);
        }
        else
        {
          PlatformLog("Unable to initialize GLEW: %s\n",
glewGetErrorString(GlewResult));
        }

        SDL_GL_DeleteContext(GLContext);
      }
      else
      {
        PlatformLog("Failed to create OpenGL context: %s", SDL_GetError());
      }

      SDL_DestroyWindow(Window);
    }
    else
    {
      PlatformLog("Failed to create window: %s", SDL_GetError());
    }

SDL_Quit();
  }
  else
  {
    PlatformLog("Failed to initialize SDL: %s", SDL_GetError());
  }

  return EXIT_SUCCESS;
}

On Sun, Nov 10, 2019 at 2:48 AM Alan Mackenzie <acm@muc.de> wrote:

> Hello, Eric.
>
> Now for a top-post.  ;-)
>
> I think I may have found the cause of the bug.  It may have been caused
> by overwriting lisp list structure, rather than creating new (parallel)
> list structures - a sort of corruption.  See below for a fuller
> explanation.  This hypothesis is entirely consistent with the observed
> result (spurious topmost-intro's).
>
> Would you please apply the patch below and byte-compile the result.  It
> suffices just to compile cc-engine.el.  (If you want any help with
> applying the patch or byte compiling, feel free to send me private
> email.)
>
> Then please try out the patched CC Mode for however long it has taken,
> in the past, to see the sort of bug you reported, and then somewhat
> longer.  If the bug fails to show itself, we may well have fixed it.
> Please let me know how things are going.
>
> ############## Optional section.  An explanation #######################
>
> One of the caches CC Mode uses, "c-state-cache", keeps track of the
> nested brace structure.  It is (re)calculated on calling the lisp
> function c-parse-state.  In essence, it records the position of each
> enclosing brace in a list, the most nested first.  So if we had the
> following C++ structure:
>
>     { // 1
>     ......
>         { // 2
>         ......
>             { // 3
>             ...... <point>
>
> , and <point> was at the marked position, our c-state-cache would be the
> list of the three brace positions (P3 P2 P1).  One of its uses is
> determining if some point is at the top level or not.  If the
> c-state-cache list for that point is empty, it is at the top level.
>
> As point moves through the buffer, and c-parse-state is called from
> somewhere else, c-state-cache is "altered" in a highly optimised
> fashion.  This avoids having to scan large portions of the buffer too
> often, to determine the brace structure.  The nature of this "altering"
> is what is causing the problem.
>
> When the buffer is narrowed, say beginning with the brace 2, calling
> c-parse-state now has to return (P3 P2), because the brace 1 is now
> outside the visible portion.
>
> The suspected bug cause is the way (P3 P2 P1) is changed to (P3 P2) on
> this narrowing.  Up to now the list structure itself has been changed,
> rather than making a copy of the structure.
>
> So, what may have been happening is that CC Mode is looping through the
> c-state-cache to determine whether point is at top level.  (Being
> directly inside a class or namespace, etc., counts as "top level").  If
> point is inside brace 1, the loop will try to determine that P1 is not a
> class, etc., and return "not at top level".  However, if c-parse-state
> had been called with the above narrowing, c-state-cache is now (P3 P2),
> point appears to be outside every brace, and the loop spuriously returns
> "at top level".  This is what I think has been happening.
>
> When the code wrongly reports "at top level", we get the unwanted
> topmost-intro analyses.
>
> The solution to this is when the buffer is narrowed and we call
> c-parse-state, we make a COPY of the c-state-cache list, leaving the
> original unmolested for its original owner.  This is what the patch
> does.
>
> ############## End of optional section. ################################
>
> Here is the patch.  It should work in Emacs-26.3, even though the line
> numbers are now a bit different:
>
>
>
> diff -r 2783baa48d44 cc-engine.el
> --- a/cc-engine.el      Fri Oct 25 20:00:14 2019 +0000
> +++ b/cc-engine.el      Sun Nov 10 10:30:17 2019 +0000
> @@ -3690,7 +3690,13 @@
>                                                         ; brace pair.
>             (setq c-state-cache nil
>                   c-state-cache-good-pos c-state-min-scan-pos)
> -         (setcdr ptr nil)
> +         ;; Do not alter the original `c-state-cache' structure, since
> there
> +         ;; may be a loop suspended which is looping through that
> structure.
> +         ;; This may have been the cause of bug #37910.
> +         (let ((cdr-ptr (cdr ptr)))
> +           (setcdr ptr nil)
> +           (setq c-state-cache (copy-sequence c-state-cache))
> +           (setcdr ptr cdr-ptr))
>           (setq c-state-cache-good-pos (1+ (c-state-cache-top-lparen))))
>         )))
>
> @@ -3793,11 +3799,12 @@
>                 (setq new-cons (cons bra (1+ ce)))
>                 (cond
>                  ((consp (car c-state-cache))
> -                 (setcar c-state-cache new-cons))
> +                 (setq c-state-cache (cons new-cons (cdr c-state-cache))))
>                  ((and (numberp (car c-state-cache)) ; probably never
> happens
>                        (< ce (car c-state-cache)))
> -                 (setcdr c-state-cache
> -                         (cons new-cons (cdr c-state-cache))))
> +                 (setq c-state-cache
> +                       (cons (car c-state-cache)
> +                             (cons new-cons (cdr c-state-cache)))))
>                  (t (setq c-state-cache (cons new-cons c-state-cache)))))
>
>             ;; We haven't found a brace pair.  Record this in the cache.
> @@ -3998,7 +4005,7 @@
>         (when (and c-state-cache
>                    (consp (car c-state-cache))
>                    (> (cdar c-state-cache) upper-lim))
> -         (setcar c-state-cache (caar c-state-cache))
> +         (setq c-state-cache (cons (caar c-state-cache) (cdr
> c-state-cache)))
>           (setq scan-back-pos (car c-state-cache)
>                 cons-separated t))
>
> @@ -4135,7 +4142,7 @@
>        ;; knowledge of what's inside these braces, we have no alternative
> but
>        ;; to direct the caller to scan the buffer from the opening brace.
>        (setq pos (caar c-state-cache))
> -      (setcar c-state-cache pos)
> +      (setq c-state-cache (cons pos (cdr c-state-cache)))
>        (list (1+ pos) pos t)) ; return value.  We've just converted a
> brace pair
>                              ; entry into a { entry, so the caller needs to
>                              ; search for a brace pair before the {.
>
>
>
>
> On Sun, Oct 27, 2019 at 15:39:56 +0000, Alan Mackenzie wrote:
> > Hello, Eric.
>
> > On Thu, Oct 24, 2019 at 12:06:18 -0700, Eric Scrivner wrote:
> > > This seems related to (if not the same as) bug #5490.
>
> > > - This happens randomly and then randomly stops happening (cache expiry
> > > maybe?)
> > > - M-x revert-buffer does not fix.
>
> > Thanks for taking the trouble to report this bug, and thanks even more
> > for including so much information.
>
> > > Here's a snippet from the buffer at the time this happen, as you can
> see
> > > there seems to be a region until the end where everything becomes
> > > topmost-intro:
>
> > >       }                          // ((block-close 18328))
> > >                                 // ((statement 9560))
> > >       SDL_DestroyWindow(Window); // ((statement 9560))
> > >     }    // ((block-close 9490))
> > >     else                        // ((else-clause 9466))
> > >     {                                                             //
> > > ((substatement-open 18464))
> > >       PlatformLog("Failed to create window: %s", SDL_GetError()); //
> > > ((statement-block-intro 18473))
> > >     } // ((block-close 18473))
> > >                                 // ((topmost-intro 18576))
> > >                                 // ((topmost-intro 18576))
> > > SDL_Quit();                     // ((topmost-intro 18541))
> > >   }                             // ((topmost-intro 18548))
> > >   else                          // ((else-clause 9188))
> > >   {                             // ((substatement-open 18845))
> > >     PlatformLog("Failed to initialize SDL: %s", SDL_GetError()); //
> > > ((statement-block-intro 18901))((statement-block-intro 18724))
> > >   }                                                              //
> > > ((block-close 18901))
> > >                                 // ((topmost-intro 19093))
> > >   return EXIT_SUCCESS;          // ((topmost-intro 19093))
> > > }                               // ((topmost-intro 19242))
>
> > At the moment, all I can do is acknowledge receipt of your report.  It's
> > obviously not an easy bug to diagnose.
>
> > I can see two ways of making progress: (i) Inspecting
> > c-guess-basic-syntax, the function which analyses code and produces
> > (amongs other things) all these topmost-intro's.  It is essentially a
> > large cond form (Lisp's equivalent of a switch), and the single place
> > which produces topmost-intro comes fairly early on in this cond form;
> > (ii) Determine what aspects of a buffer do not get reinitialised after
> > evaluating M-x revert-buffer.  That could provide some clue.
>
> > [ CC Mode config dump acknowledged with thanks, but snipped ]
>
> > Just one thing.  If you haven't already done so, could you make a backup
> > copy of a buffer which triggers the bug, just in case after some future
> > edit it no longer does so.  Thanks!
>
> --
> Alan Mackenzie (Nuremberg, Germany).
>

[-- Attachment #2: Type: text/html, Size: 36000 bytes --]

^ permalink raw reply	[flat|nested] 12+ messages in thread

* bug#37910: CC Mode 5.33.2 (C++//l); CC-mode inconsistently indents everything as topmost-intro after a while
  2019-11-10 17:47     ` Eric Scrivner
@ 2019-11-10 17:50       ` Eric Scrivner
  2019-11-10 19:02       ` Alan Mackenzie
  2019-11-13 18:38       ` Alan Mackenzie
  2 siblings, 0 replies; 12+ messages in thread
From: Eric Scrivner @ 2019-11-10 17:50 UTC (permalink / raw)
  To: Alan Mackenzie; +Cc: 37910

[-- Attachment #1: Type: text/plain, Size: 31611 bytes --]

Apologies for the second email here. One other possibly symptom here: This
only seems to happen in buffers where I used the c++11 multi-line raw
string literals. All other buffers seem to be fine atm.

On Sun, Nov 10, 2019 at 9:47 AM Eric Scrivner <eric.t.scrivner@gmail.com>
wrote:

> I've applied the patch and run emacs with it. Unfortunately, it doesn't
> seem to solve the issue. One the bright side, I believe I had buffers which
> can reliably reproduce the issue (at least locally). I've copied one such
> file below. The issue can be reproduced by simply going to the end of
> `main` and attempting to indent the `SQL_Quit()` statement. At first it
> attempts to overindent the line as `statement-cont`, then if I go up and
> attempt to indent the `PlatformLog` in the else statement then return to
> the SDL_Quit and indent it, it has become `topmost-intro`. Apologies for
> the long length of the file, but hopefully it can help with reproduction:
>
> #include "engine_math.h"
> #include "engine_types.h"
> #include "platform.h"
>
> #include <SDL.h>
> #include <SDL_opengl.h>
>
> #include <sys/mman.h>
> #include <x86intrin.h>
>
> const f32 VIEWPORT_WIDTH = 1920;
> const f32 VIEWPORT_HEIGHT = 1080;
>
>
> ///////////////////////////////////////////////////////////////////////////////
>
> typedef struct {
>   game_input Input;
> } sdl2_input_state;
>
> typedef struct {
>   i32 ToneHz;
>   i32 ToneVolume;
>   i32 SampleIndex;
>   i32 SamplesPerSecond;
>   i32 BytesPerSample;
>   i32 WavePeriod;
>   f64 SineLocation;
> } sdl2_audio_config;
>
> typedef struct {
>   u8* Buffer;
>   i32 Size;
>   i32 ReadCursor;
>   i32 WriteCursor;
>   b32 IsPlaying;
>   SDL_AudioDeviceID DeviceID;
>   sdl2_audio_config* AudioConfig;
> } sdl2_audio_buffer;
>
> #if HYPERBOREAN_DEBUG
> typedef struct {
>   v2 Pos;
>   v3 Color;
> } sdl2_debug_colored_vertex;
>
> typedef struct {
>   i32 PlayCursor;
>   i32 WriteCursor;
>   sdl2_debug_colored_vertex V[2];
> } sdl2_debug_time_marker;
> #endif
>
>
> ///////////////////////////////////////////////////////////////////////////////
>
> global_variable b32 GlobalRunning;
> global_variable sdl2_input_state GlobalGameInput;
> global_variable sdl2_audio_buffer GlobalAudioBuffer;
>
>
> ///////////////////////////////////////////////////////////////////////////////
>
> void PlatformLog(const char* const Format, ...)
> {
>   va_list args;
>   va_start(args, Format);
>
>   SDL_LogMessageV(
>     SDL_LOG_CATEGORY_APPLICATION,
>     SDL_LOG_PRIORITY_INFO,
>     Format,
>     args
>   );
>
>   va_end(args);
> }
>
> b32 PlatformReadEntireFile(const char* path, platform_entire_file* Result)
> {
>   FILE* file = fopen(path, "r");
>
>   if (file == NULL) {
>     return false;
>   }
>
>   fseek(file, 0, SEEK_END);
>   Result->Size = ftell(file);
>   fseek(file, 0, SEEK_SET);
>
>   Result->Contents = (u8*)malloc(Result->Size);
>   fread(Result->Contents, Result->Size, 1, file);
>   fclose(file);
>
>   return true;
> }
>
> void PlatformFreeEntireFile(platform_entire_file* Result) {
>   if (Result->Contents) {
>     free(Result->Contents);
>     Result->Contents = NULL;
>     Result->Size = 0;
>   }
> }
>
> void PlatformAudioLock() {
>   SDL_LockAudioDevice(GlobalAudioBuffer.DeviceID);
> }
>
> void PlatformAudioUnlock() {
>   SDL_UnlockAudioDevice(GlobalAudioBuffer.DeviceID);
> }
>
>
> ///////////////////////////////////////////////////////////////////////////////
>
> internal void *SDL2VirtualAlloc(u64 SizeBytes, void *BaseAddress = NULL) {
>   return mmap(BaseAddress, SizeBytes, PROT_READ | PROT_WRITE,
>               MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
> }
>
>
> ///////////////////////////////////////////////////////////////////////////////
>
> internal void SDL2VirtualFree(void *Buffer, u64 SizeBytes) {
>   munmap(Buffer, SizeBytes);
> }
>
>
> ///////////////////////////////////////////////////////////////////////////////
>
> internal bool SDL2VirtualAllocSucceeded(void *Value) {
>   return Value != MAP_FAILED;
> }
>
>
> ///////////////////////////////////////////////////////////////////////////////
>
> internal void SDL2FillAudioDeviceBuffer(void *UserData, u8 *DeviceBuffer,
>                                         i32 Length) {
>   Assert(UserData != NULL);
>   Assert(DeviceBuffer != NULL);
>
>   SDL_memset(DeviceBuffer, 0, Length);
>
>   sdl2_audio_buffer *AudioBuffer = (sdl2_audio_buffer *)UserData;
>
>   // Keep track of two regions. Region1 contains everything from the
> current
>   // ReadCursor up until, potentially, the end of the buffer. Region2 only
>   // exists if we need to circle back around. It contains all the data
> from the
>   // beginning of the buffer up until sufficient bytes are read to meet
> Length.
>   int Region1Size = Length;
>   int Region2Size = 0;
>   if (AudioBuffer->ReadCursor + Length > AudioBuffer->Size) {
>     // Handle looping back from the beginning.
>     Region1Size = AudioBuffer->Size - AudioBuffer->ReadCursor;
>     Region2Size = Length - Region1Size;
>   }
>
>   SDL_memcpy(DeviceBuffer, (AudioBuffer->Buffer + AudioBuffer->ReadCursor),
>              Region1Size);
>   SDL_memcpy(&DeviceBuffer[Region1Size], AudioBuffer->Buffer, Region2Size);
>
>   AudioBuffer->ReadCursor =
>       (AudioBuffer->ReadCursor + Length) % AudioBuffer->Size;
> }
>
>
> ///////////////////////////////////////////////////////////////////////////////
>
> internal void SDL2InitializeAudio(sdl2_audio_buffer *AudioBuffer) {
>   AudioBuffer->Size = AudioBuffer->AudioConfig->SamplesPerSecond *
>                       AudioBuffer->AudioConfig->BytesPerSample;
>   AudioBuffer->WriteCursor = AudioBuffer->AudioConfig->BytesPerSample;
>   AudioBuffer->ReadCursor = 0;
>   AudioBuffer->Buffer = (u8 *)SDL2VirtualAlloc(AudioBuffer->Size);
>   Assert(SDL2VirtualAllocSucceeded(AudioBuffer->Buffer));
>
>   SDL_AudioSpec AudioSettings = {};
>   AudioSettings.freq = AudioBuffer->AudioConfig->SamplesPerSecond;
>   AudioSettings.format = AUDIO_S16;
>   AudioSettings.channels = 2;
>   // NOTE: Reduce the number of samples here to decrease audio lag by
> causing
>   // SDL to request audio more frequently.
>   AudioSettings.samples = 256;
>   AudioSettings.callback = &SDL2FillAudioDeviceBuffer;
>   AudioSettings.userdata = (void *)AudioBuffer;
>
>   SDL_AudioSpec ObtainedSettings = {};
>   AudioBuffer->DeviceID =
>       SDL_OpenAudioDevice(NULL, 0, &AudioSettings, &ObtainedSettings, 0);
>
>   if (AudioSettings.format != ObtainedSettings.format) {
>     SDL_Log("Could not open audio device: %s", SDL_GetError());
>     exit(1);
>   }
> }
>
>
> ///////////////////////////////////////////////////////////////////////////////
>
> internal void SDL2Cleanup(sdl2_audio_buffer *AudioBuffer,
>                           sdl2_input_state *InputState) {
>   if (AudioBuffer->Buffer) {
>     SDL2VirtualFree(AudioBuffer->Buffer, AudioBuffer->Size);
>   }
>
>   SDL_CloseAudioDevice(AudioBuffer->DeviceID);
> }
>
>
> ///////////////////////////////////////////////////////////////////////////////
>
> internal void SDL2ProcessKeyDown(game_button_state *State, b32 IsDown) {
>   Assert(IsDown != State->EndedDown);
>   State->EndedDown = IsDown;
>   ++State->HalfTransitionCount;
> }
>
>
> ///////////////////////////////////////////////////////////////////////////////
>
> #if HYPERBOREAN_DEBUG
> internal u32 SDL2DebugVAO;
> internal u32 SDL2DebugVBO;
>
> internal void SDL2DrawSoundBufferMarker(sdl2_audio_buffer *AudioBuffer,
> f32 C,
>                                         f32 PadX, f32 Top, f32 Bottom,
>                                         i32 Value,
>                                         sdl2_debug_colored_vertex
> *Vertices,
>                                         v3 Color, u32 Shader) {
>   Assert(Value < AudioBuffer->Size);
>   f32 XReal = C * Value + PadX;
>   Vertices[0].Pos = {{XReal, Top}};
>   Vertices[0].Color = Color;
>   Vertices[1].Pos = {{XReal, Bottom}};
>   Vertices[1].Color = Color;
>   glUseProgram(Shader);
>   m4x4 Projection =
>       OrthographicProjection(0, VIEWPORT_WIDTH, 0, VIEWPORT_HEIGHT, -1.0,
> 1.0);
>   GLint ProjectionLoc = glGetUniformLocation(Shader, "Projection");
>   glUniformMatrix4fv(ProjectionLoc, 1, GL_FALSE, (float *)Projection.E);
>   glBindBuffer(GL_ARRAY_BUFFER, SDL2DebugVBO);
>   glBufferSubData(GL_ARRAY_BUFFER, 0, 2 *
> sizeof(sdl2_debug_colored_vertex),
>                   Vertices);
>   glBindVertexArray(SDL2DebugVAO);
>   glDrawArrays(GL_LINES, 0, 2);
>   glBindVertexArray(0);
> }
>
> internal void SDL2DebugSyncDisplay(sdl2_audio_buffer *AudioBuffer,
>                                    u32 MarkerCount,
>                                    sdl2_debug_time_marker *Markers,
>                                    f32 TargetSecondsPerFrame, u32 Shader) {
>   f32 PadX = 16;
>   f32 PadY = 16;
>
>   f32 Top = PadY;
>   f32 Bottom = VIEWPORT_HEIGHT - PadY;
>
>   f32 C = (f32)(VIEWPORT_WIDTH - 2 * PadX) / (f32)AudioBuffer->Size;
>   for (u32 MarkerIndex = 0; MarkerIndex < MarkerCount; ++MarkerIndex) {
>     sdl2_debug_time_marker *Marker = &Markers[MarkerIndex];
>
>     SDL2DrawSoundBufferMarker(AudioBuffer, C, PadX, Top, Bottom,
>                               Marker->PlayCursor, Marker->V, {{1.0, 1.0,
> 1.0}},
>                               Shader);
>     SDL2DrawSoundBufferMarker(AudioBuffer, C, PadX, Top, Bottom,
>                               Marker->WriteCursor, Marker->V, {{1.0, 1.0,
> 0.0}},
>                               Shader);
>   }
> }
> #endif
>
>
> ///////////////////////////////////////////////////////////////////////////////
>
> internal int
> SDL2GetWindowRefreshRate(SDL_Window *Window)
> {
>     SDL_DisplayMode Mode;
>     int DisplayIndex = SDL_GetWindowDisplayIndex(Window);
>     // If we can't find the refresh rate, we'll return this:
>     int DefaultRefreshRate = 60;
>     if (SDL_GetDesktopDisplayMode(DisplayIndex, &Mode) != 0)
>     {
>         return DefaultRefreshRate;
>     }
>     if (Mode.refresh_rate == 0)
>     {
>         return DefaultRefreshRate;
>     }
>     return Mode.refresh_rate;
> }
>
>
> ///////////////////////////////////////////////////////////////////////////////
>
> internal void SDL2HandleEvent(SDL_Event* Event, sdl2_input_state*
> InputState)
> {
>   if (Event->type == SDL_QUIT)
>   {
>     GlobalRunning = false;
>   }
>   else if (Event->type == SDL_KEYDOWN || Event->type == SDL_KEYUP)
>   {
>     SDL_Keycode KeyCode = Event->key.keysym.sym;
>     bool IsDown = Event->key.state == SDL_PRESSED;
>     bool WasDown = (Event->key.state == SDL_RELEASED || Event->key.repeat
> != 0);
>
>     // Eat key repeats
>     if (IsDown != WasDown)
>     {
>       if (KeyCode == SDLK_LEFT)
>       {
>         SDL2ProcessKeyDown(&InputState->Input.Controller.MoveLeft, IsDown);
>       }
>       else if (KeyCode == SDLK_RIGHT)
>       {
>         SDL2ProcessKeyDown(&InputState->Input.Controller.MoveRight,
> IsDown);
>       }
>       else if (KeyCode == SDLK_UP)
>       {
>         SDL2ProcessKeyDown(&InputState->Input.Controller.MoveUp, IsDown);
>       }
>       else if (KeyCode == SDLK_DOWN)
>       {
>         SDL2ProcessKeyDown(&InputState->Input.Controller.MoveDown, IsDown);
>       }
>       else if (KeyCode == SDLK_SPACE)
>       {
>         SDL2ProcessKeyDown(&InputState->Input.Controller.ActionDown,
> IsDown);
>       }
>       else if (KeyCode == SDLK_ESCAPE)
>       {
>         SDL2ProcessKeyDown(&InputState->Input.Controller.Back, IsDown);
>       }
>     }
>   }
>   else if (Event->type == SDL_MOUSEMOTION)
>   {
>     InputState->Input.MouseP.X = Event->motion.x;
>     InputState->Input.MouseP.Y = Event->motion.y;
>   }
>   else if (Event->type == SDL_MOUSEBUTTONDOWN || Event->type ==
> SDL_MOUSEBUTTONUP)
>   {
>     bool IsDown = Event->button.state == SDL_PRESSED;
>     bool WasDown = (Event->button.state == SDL_RELEASED ||
> Event->button.clicks != 1);
>
>     if (IsDown != WasDown) {
>       if (Event->button.button == SDL_BUTTON_LEFT) {
>         SDL2ProcessKeyDown(&InputState->Input.MouseLeft, IsDown);
>       }
>       if (Event->button.button == SDL_BUTTON_RIGHT) {
>         SDL2ProcessKeyDown(&InputState->Input.MouseRight, IsDown);
>       }
>     }
>   }
> }
>
>
> ///////////////////////////////////////////////////////////////////////////////
>
> int main() {
>   if (SDL_Init(SDL_INIT_EVERYTHING) == 0)
>   {
>     SDL_Window* Window = SDL_CreateWindow(
>       "Hyperborean",
>       SDL_WINDOWPOS_UNDEFINED,
>       SDL_WINDOWPOS_UNDEFINED,
>       VIEWPORT_WIDTH,
>       VIEWPORT_HEIGHT,
>       SDL_WINDOW_OPENGL | SDL_WINDOW_ALLOW_HIGHDPI
>     );
>
>     if (Window != NULL)
>     {
>       SDL_GLContext GLContext = SDL_GL_CreateContext(Window);
>
>       if (GLContext != NULL)
>       {
>         GLenum GlewResult = glewInit();
>         if (GlewResult == GLEW_OK)
>         {
>           PlatformLog("OpenGL Vendor: %s", glGetString(GL_VENDOR));
>           PlatformLog("OpenGL Version: %s", glGetString(GL_VERSION));
>           PlatformLog("OpenGL Renderer: %s", glGetString(GL_RENDERER));
>
>           if (SDL_GL_SetSwapInterval(1) != 0) {
>             PlatformLog("Unable to SetSwapInterval: %s", SDL_GetError());
>           }
>
>           game_memory GameMemory = {};
>
> #if HYPERBOREAN_INTERNAL
>           // NOTE: Hard-code the base address so locations remain constant
> between runs
>           void* BaseAddress = (void*)Terabytes(2);
> #else
>           void* BaseAddress = NULL;
> #endif
>
>           GameMemory.PermanentStorageSize = Megabytes(512);
>           GameMemory.TransientStorageSize = Gigabytes((u64)4);
>
>           u64 TotalSize =
>             GameMemory.PermanentStorageSize +
> GameMemory.TransientStorageSize;
>           GameMemory.PermanentStorage = SDL2VirtualAlloc(TotalSize,
> BaseAddress);
>           Assert(SDL2VirtualAllocSucceeded(GameMemory.PermanentStorage));
>           GameMemory.TransientStorage = (
>             (u8*)GameMemory.PermanentStorage +
> GameMemory.PermanentStorageSize);
>
>           // Initialize keyboard input
>           GlobalGameInput = {};
>           GlobalGameInput.Input.WindowDim.W = VIEWPORT_WIDTH;
>           GlobalGameInput.Input.WindowDim.H = VIEWPORT_HEIGHT;
>           GlobalGameInput.Input.Controller.IsConnected = true;
>           GlobalGameInput.Input.Controller.IsAnalog = true;
>
>           u32 MaxVolume = 3000;
>
>           GlobalAudioBuffer = {};
>           sdl2_audio_config AudioConfig = {};
>           AudioConfig.ToneHz = 261;
>           AudioConfig.ToneVolume = 0.10 * MaxVolume;
>           AudioConfig.SampleIndex = 0;
>           AudioConfig.SamplesPerSecond = 48000;
>           AudioConfig.BytesPerSample = 2 * sizeof(i16);
>           AudioConfig.WavePeriod = AudioConfig.SamplesPerSecond /
> AudioConfig.ToneHz;
>           GlobalAudioBuffer.AudioConfig = &AudioConfig;
>
>           SDL2InitializeAudio(&GlobalAudioBuffer);
>
>           // Get screen refresh rate.
>           PlatformLog("Refresh Rate: %d Hz\n",
> SDL2GetWindowRefreshRate(Window));
>           i32 GameUpdateHz = 60;
>           f32 TargetSecondsPerFrame = 1.0f / (float)GameUpdateHz;
>           PlatformLog("Target Secs/Frame: %f\n", TargetSecondsPerFrame);
>
>           // TODO(eric): Feed DPI information into our game and use it for
> scaling.
>           f32 DDPI = 0;
>           f32 HDPI = 0;
>           f32 VDPI = 0;
>           if (SDL_GetDisplayDPI(0, &DDPI, &HDPI, &VDPI) != 0) {
>             PlatformLog("Unable to get display DPI: %s", SDL_GetError());
>           }
>           PlatformLog("Display DPI: %f - %f x %f", DDPI, HDPI, VDPI);
>
>           f32 FPS = 0;
>           f32 MPF = 0;
>
>           char WindowTitle[256] = {};
>           u64 BeginCounter = SDL_GetPerformanceCounter();
>           u64 PerfCounterFrequency = SDL_GetPerformanceFrequency();
>           u64 BeginCycles = __rdtsc();
>
> #if  HYPERBOREAN_DEBUG
>           game_state* State = (game_state*)GameMemory.PermanentStorage;
>           u32 DebugMarkerShader = CompileShaders(
>             &State->TransientArena,
>             R"END(
> #version 430 core
> layout (location = 0) in vec2 Position;
> layout (location = 1) in vec3 Color;
> uniform mat4 Projection;
>
> out vec3 FragmentColor;
>
> void main()
> {
>   FragmentColor = Color;
>   gl_Position = vec4(Position, 0.0, 1.0) * Projection;
> }
> )END",
>             R"END(
> #version 430 core
> in vec3 FragmentColor;
>
> out vec4 ResultingColor;
>
> void main()
> {
>   ResultingColor = vec4(FragmentColor, 1.0);
> }
> )END"
>           );
>           u32 DebugTimeMarkerIndex = 0;
>           sdl2_debug_time_marker DebugTimeMarkers[GameUpdateHz / 2] = {0};
>           PlatformLog("Num Markers: %d\n", ArrayCount(DebugTimeMarkers));
>
>           glGenVertexArrays(1, &SDL2DebugVAO);
>           glBindVertexArray(SDL2DebugVAO);
>
>           glGenBuffers(1, &SDL2DebugVBO);
>           glBindBuffer(GL_ARRAY_BUFFER, SDL2DebugVBO);
>           glBufferData(GL_ARRAY_BUFFER,
> 2*sizeof(sdl2_debug_colored_vertex), NULL, GL_DYNAMIC_DRAW);
>           glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE,
> sizeof(sdl2_debug_colored_vertex), NULL);
>           glEnableVertexAttribArray(0);
>           glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE,
> sizeof(sdl2_debug_colored_vertex), (void*)sizeof(v2));
>           glEnableVertexAttribArray(1);
>
>           glBindBuffer(GL_ARRAY_BUFFER, 0);
>           glBindVertexArray(0);
> #endif
>
>           u32 BeginTimeMs = SDL_GetTicks();
>
>           GlobalRunning = true;
>           SDL_Event event;
>           while (GlobalRunning) {
>             while (SDL_PollEvent(&event)) {
>               SDL2HandleEvent(&event, &GlobalGameInput);
>             }
>
>             game_input GameInput = {};
>             memcpy(&GameInput, &GlobalGameInput.Input, sizeof(game_input));
>
>             game_audio_buffer AudioBuffer = {};
>             AudioBuffer.Buffer = GlobalAudioBuffer.Buffer;
>             AudioBuffer.Size = GlobalAudioBuffer.Size;
>             AudioBuffer.ReadCursor = GlobalAudioBuffer.ReadCursor;
>             AudioBuffer.WriteCursor = GlobalAudioBuffer.WriteCursor;
>             AudioBuffer.ToneVolume =
> GlobalAudioBuffer.AudioConfig->ToneVolume;
>             AudioBuffer.SamplesPerSecond =
>               GlobalAudioBuffer.AudioConfig->SamplesPerSecond;
>             AudioBuffer.BytesPerSample =
> GlobalAudioBuffer.AudioConfig->BytesPerSample;
>
>             SDL_LockAudioDevice(GlobalAudioBuffer.DeviceID);
>             u32 EndTimeMs = SDL_GetTicks();
>             GameInput.DeltaTimeSecs = (EndTimeMs - BeginTimeMs) / 1000.0f;
>             GameUpdateAndRender(&GameMemory, &GameInput, &AudioBuffer);
>             BeginTimeMs = EndTimeMs;
>             SDL_UnlockAudioDevice(GlobalAudioBuffer.DeviceID);
>
>             // NOTE: Unpause for the first time here to avoid startup lag
> due
>             // to SDL already playing audio from an empty buffer. Unpausing
>             // here allows audio to begin playing immediately at
> application
>             // start.
>             if (!GlobalAudioBuffer.IsPlaying)
>             {
>               SDL_PauseAudioDevice(GlobalAudioBuffer.DeviceID, 0);
>               GlobalAudioBuffer.IsPlaying = true;
>             }
>
>             // NOTE: Copy back out the write cursor state for the circular
> audio buffer
>             GlobalAudioBuffer.WriteCursor = AudioBuffer.WriteCursor;
>
>             for (size_t ButtonIndex = 0;
>                  ButtonIndex <
> ArrayCount(GlobalGameInput.Input.Controller.Buttons);
>                  ButtonIndex++)
>             {
>
> GlobalGameInput.Input.Controller.Buttons[ButtonIndex].HalfTransitionCount =
> 0;
>             }
>
>             if (GameInput.QuitRequested)
>             {
>               GlobalRunning = false;
>             }
>
> #if HYPERBOREAN_DEBUG
>             {
>               sdl2_debug_time_marker* Marker =
> &DebugTimeMarkers[DebugTimeMarkerIndex++];
>               if (DebugTimeMarkerIndex >= ArrayCount(DebugTimeMarkers)) {
>                 DebugTimeMarkerIndex = 0;
>               }
>
>               Marker->PlayCursor = GlobalAudioBuffer.ReadCursor;
>               Marker->WriteCursor = GlobalAudioBuffer.WriteCursor;
>
>               SDL2DebugSyncDisplay(&GlobalAudioBuffer,
> ArrayCount(DebugTimeMarkers), DebugTimeMarkers, TargetSecondsPerFrame,
> DebugMarkerShader);
>             }
> #endif
>
>             SDL_GL_SwapWindow(Window);
>
>             u64 EndCounter = SDL_GetPerformanceCounter();
>             u64 CounterElapsed = EndCounter - BeginCounter;
>
>             u64 EndCycles = __rdtsc();
>             u64 CyclesElapsed = EndCycles - BeginCycles;
>             FPS = PerfCounterFrequency / (float)CounterElapsed;
>             f32 SPF = (float)CounterElapsed / PerfCounterFrequency;
>             MPF = 1000.0f * CounterElapsed / (double)PerfCounterFrequency;
>             i32 MCPF = SafeTruncateUInt64(CyclesElapsed / (1000 * 1000));
>
>             BeginCounter = EndCounter;
>             BeginCycles = EndCycles;
>             sprintf(
>               WindowTitle,
>               "Hyperborean - %0.02f f/s, %0.04f s/f, %0.02f ms/f, %d mc/f",
>               FPS, SPF, MPF, MCPF
>             );
>             SDL_SetWindowTitle(Window, WindowTitle);
>           }
>
>           GameCleanup(&GameMemory);
> #if HYPERBOREAN_DEBUG
>           glDeleteBuffers(1, &SDL2DebugVAO);
>           glDeleteBuffers(1, &SDL2DebugVBO);
>           glDeleteShader(DebugMarkerShader);
> #endif
>
>           SDL2VirtualFree(GameMemory.PermanentStorage,
> GameMemory.PermanentStorageSize);
>           SDL2VirtualFree(GameMemory.TransientStorage,
> GameMemory.TransientStorageSize);
>
>           SDL2Cleanup(&GlobalAudioBuffer, &GlobalGameInput);
>         }
>         else
>         {
>           PlatformLog("Unable to initialize GLEW: %s\n",
> glewGetErrorString(GlewResult));
>         }
>
>         SDL_GL_DeleteContext(GLContext);
>       }
>       else
>       {
>         PlatformLog("Failed to create OpenGL context: %s", SDL_GetError());
>       }
>
>       SDL_DestroyWindow(Window);
>     }
>     else
>     {
>       PlatformLog("Failed to create window: %s", SDL_GetError());
>     }
>
> SDL_Quit();
>   }
>   else
>   {
>     PlatformLog("Failed to initialize SDL: %s", SDL_GetError());
>   }
>
>   return EXIT_SUCCESS;
> }
>
> On Sun, Nov 10, 2019 at 2:48 AM Alan Mackenzie <acm@muc.de> wrote:
>
>> Hello, Eric.
>>
>> Now for a top-post.  ;-)
>>
>> I think I may have found the cause of the bug.  It may have been caused
>> by overwriting lisp list structure, rather than creating new (parallel)
>> list structures - a sort of corruption.  See below for a fuller
>> explanation.  This hypothesis is entirely consistent with the observed
>> result (spurious topmost-intro's).
>>
>> Would you please apply the patch below and byte-compile the result.  It
>> suffices just to compile cc-engine.el.  (If you want any help with
>> applying the patch or byte compiling, feel free to send me private
>> email.)
>>
>> Then please try out the patched CC Mode for however long it has taken,
>> in the past, to see the sort of bug you reported, and then somewhat
>> longer.  If the bug fails to show itself, we may well have fixed it.
>> Please let me know how things are going.
>>
>> ############## Optional section.  An explanation #######################
>>
>> One of the caches CC Mode uses, "c-state-cache", keeps track of the
>> nested brace structure.  It is (re)calculated on calling the lisp
>> function c-parse-state.  In essence, it records the position of each
>> enclosing brace in a list, the most nested first.  So if we had the
>> following C++ structure:
>>
>>     { // 1
>>     ......
>>         { // 2
>>         ......
>>             { // 3
>>             ...... <point>
>>
>> , and <point> was at the marked position, our c-state-cache would be the
>> list of the three brace positions (P3 P2 P1).  One of its uses is
>> determining if some point is at the top level or not.  If the
>> c-state-cache list for that point is empty, it is at the top level.
>>
>> As point moves through the buffer, and c-parse-state is called from
>> somewhere else, c-state-cache is "altered" in a highly optimised
>> fashion.  This avoids having to scan large portions of the buffer too
>> often, to determine the brace structure.  The nature of this "altering"
>> is what is causing the problem.
>>
>> When the buffer is narrowed, say beginning with the brace 2, calling
>> c-parse-state now has to return (P3 P2), because the brace 1 is now
>> outside the visible portion.
>>
>> The suspected bug cause is the way (P3 P2 P1) is changed to (P3 P2) on
>> this narrowing.  Up to now the list structure itself has been changed,
>> rather than making a copy of the structure.
>>
>> So, what may have been happening is that CC Mode is looping through the
>> c-state-cache to determine whether point is at top level.  (Being
>> directly inside a class or namespace, etc., counts as "top level").  If
>> point is inside brace 1, the loop will try to determine that P1 is not a
>> class, etc., and return "not at top level".  However, if c-parse-state
>> had been called with the above narrowing, c-state-cache is now (P3 P2),
>> point appears to be outside every brace, and the loop spuriously returns
>> "at top level".  This is what I think has been happening.
>>
>> When the code wrongly reports "at top level", we get the unwanted
>> topmost-intro analyses.
>>
>> The solution to this is when the buffer is narrowed and we call
>> c-parse-state, we make a COPY of the c-state-cache list, leaving the
>> original unmolested for its original owner.  This is what the patch
>> does.
>>
>> ############## End of optional section. ################################
>>
>> Here is the patch.  It should work in Emacs-26.3, even though the line
>> numbers are now a bit different:
>>
>>
>>
>> diff -r 2783baa48d44 cc-engine.el
>> --- a/cc-engine.el      Fri Oct 25 20:00:14 2019 +0000
>> +++ b/cc-engine.el      Sun Nov 10 10:30:17 2019 +0000
>> @@ -3690,7 +3690,13 @@
>>                                                         ; brace pair.
>>             (setq c-state-cache nil
>>                   c-state-cache-good-pos c-state-min-scan-pos)
>> -         (setcdr ptr nil)
>> +         ;; Do not alter the original `c-state-cache' structure, since
>> there
>> +         ;; may be a loop suspended which is looping through that
>> structure.
>> +         ;; This may have been the cause of bug #37910.
>> +         (let ((cdr-ptr (cdr ptr)))
>> +           (setcdr ptr nil)
>> +           (setq c-state-cache (copy-sequence c-state-cache))
>> +           (setcdr ptr cdr-ptr))
>>           (setq c-state-cache-good-pos (1+ (c-state-cache-top-lparen))))
>>         )))
>>
>> @@ -3793,11 +3799,12 @@
>>                 (setq new-cons (cons bra (1+ ce)))
>>                 (cond
>>                  ((consp (car c-state-cache))
>> -                 (setcar c-state-cache new-cons))
>> +                 (setq c-state-cache (cons new-cons (cdr
>> c-state-cache))))
>>                  ((and (numberp (car c-state-cache)) ; probably never
>> happens
>>                        (< ce (car c-state-cache)))
>> -                 (setcdr c-state-cache
>> -                         (cons new-cons (cdr c-state-cache))))
>> +                 (setq c-state-cache
>> +                       (cons (car c-state-cache)
>> +                             (cons new-cons (cdr c-state-cache)))))
>>                  (t (setq c-state-cache (cons new-cons c-state-cache)))))
>>
>>             ;; We haven't found a brace pair.  Record this in the cache.
>> @@ -3998,7 +4005,7 @@
>>         (when (and c-state-cache
>>                    (consp (car c-state-cache))
>>                    (> (cdar c-state-cache) upper-lim))
>> -         (setcar c-state-cache (caar c-state-cache))
>> +         (setq c-state-cache (cons (caar c-state-cache) (cdr
>> c-state-cache)))
>>           (setq scan-back-pos (car c-state-cache)
>>                 cons-separated t))
>>
>> @@ -4135,7 +4142,7 @@
>>        ;; knowledge of what's inside these braces, we have no alternative
>> but
>>        ;; to direct the caller to scan the buffer from the opening brace.
>>        (setq pos (caar c-state-cache))
>> -      (setcar c-state-cache pos)
>> +      (setq c-state-cache (cons pos (cdr c-state-cache)))
>>        (list (1+ pos) pos t)) ; return value.  We've just converted a
>> brace pair
>>                              ; entry into a { entry, so the caller needs
>> to
>>                              ; search for a brace pair before the {.
>>
>>
>>
>>
>> On Sun, Oct 27, 2019 at 15:39:56 +0000, Alan Mackenzie wrote:
>> > Hello, Eric.
>>
>> > On Thu, Oct 24, 2019 at 12:06:18 -0700, Eric Scrivner wrote:
>> > > This seems related to (if not the same as) bug #5490.
>>
>> > > - This happens randomly and then randomly stops happening (cache
>> expiry
>> > > maybe?)
>> > > - M-x revert-buffer does not fix.
>>
>> > Thanks for taking the trouble to report this bug, and thanks even more
>> > for including so much information.
>>
>> > > Here's a snippet from the buffer at the time this happen, as you can
>> see
>> > > there seems to be a region until the end where everything becomes
>> > > topmost-intro:
>>
>> > >       }                          // ((block-close 18328))
>> > >                                 // ((statement 9560))
>> > >       SDL_DestroyWindow(Window); // ((statement 9560))
>> > >     }    // ((block-close 9490))
>> > >     else                        // ((else-clause 9466))
>> > >     {                                                             //
>> > > ((substatement-open 18464))
>> > >       PlatformLog("Failed to create window: %s", SDL_GetError()); //
>> > > ((statement-block-intro 18473))
>> > >     } // ((block-close 18473))
>> > >                                 // ((topmost-intro 18576))
>> > >                                 // ((topmost-intro 18576))
>> > > SDL_Quit();                     // ((topmost-intro 18541))
>> > >   }                             // ((topmost-intro 18548))
>> > >   else                          // ((else-clause 9188))
>> > >   {                             // ((substatement-open 18845))
>> > >     PlatformLog("Failed to initialize SDL: %s", SDL_GetError()); //
>> > > ((statement-block-intro 18901))((statement-block-intro 18724))
>> > >   }                                                              //
>> > > ((block-close 18901))
>> > >                                 // ((topmost-intro 19093))
>> > >   return EXIT_SUCCESS;          // ((topmost-intro 19093))
>> > > }                               // ((topmost-intro 19242))
>>
>> > At the moment, all I can do is acknowledge receipt of your report.  It's
>> > obviously not an easy bug to diagnose.
>>
>> > I can see two ways of making progress: (i) Inspecting
>> > c-guess-basic-syntax, the function which analyses code and produces
>> > (amongs other things) all these topmost-intro's.  It is essentially a
>> > large cond form (Lisp's equivalent of a switch), and the single place
>> > which produces topmost-intro comes fairly early on in this cond form;
>> > (ii) Determine what aspects of a buffer do not get reinitialised after
>> > evaluating M-x revert-buffer.  That could provide some clue.
>>
>> > [ CC Mode config dump acknowledged with thanks, but snipped ]
>>
>> > Just one thing.  If you haven't already done so, could you make a backup
>> > copy of a buffer which triggers the bug, just in case after some future
>> > edit it no longer does so.  Thanks!
>>
>> --
>> Alan Mackenzie (Nuremberg, Germany).
>>
>

[-- Attachment #2: Type: text/html, Size: 36589 bytes --]

^ permalink raw reply	[flat|nested] 12+ messages in thread

* bug#37910: CC Mode 5.33.2 (C++//l); CC-mode inconsistently indents everything as topmost-intro after a while
  2019-11-10 17:47     ` Eric Scrivner
  2019-11-10 17:50       ` Eric Scrivner
@ 2019-11-10 19:02       ` Alan Mackenzie
  2019-11-13 18:38       ` Alan Mackenzie
  2 siblings, 0 replies; 12+ messages in thread
From: Alan Mackenzie @ 2019-11-10 19:02 UTC (permalink / raw)
  To: Eric Scrivner; +Cc: 37910

Hello, Eric.

On Sun, Nov 10, 2019 at 09:47:35 -0800, Eric Scrivner wrote:
> I've applied the patch and run emacs with it. Unfortunately, it doesn't
> seem to solve the issue.

That's a pity.  But I think I will commit that patch anyway, since it is
an accident waiting to happen if I don't.

> On the bright side, I believe I had buffers which can reliably
> reproduce the issue (at least locally).

THAT'S ABSOLUTELY BRILLIANT!!!  :-)

> I've copied one such file below. The issue can be reproduced by simply
> going to the end of `main` and attempting to indent the `SQL_Quit()`
> statement. At first it attempts to overindent the line as
> `statement-cont`, then if I go up and attempt to indent the
> `PlatformLog` in the else statement then return to the SDL_Quit and
> indent it, it has become `topmost-intro`.  Apologies for the long
> length of the file, but hopefully it can help with reproduction:

No need for any apology whatsoever.  I can indeed reproduce the bug.

I was right, in that the bug is in the c-state-cache mechanism.  As you
mentioned in your other post, the bug is triggered by C++ raw strings.
When I had a look at some CC Mode debugging output, it was apparent that
some of the braces inside a raw string had been recorded as brace
positions, which is clearly wrong.

So this bug, which was up till recently unreproducible, is now just a
matter of (tedious) debugging.  That's a tremendous advance!  If we were
on the same continent, I'd ask you over for a drink!

Have a great Sunday afternoon!

[ .... ]

--
Alan Mackenzie (Nuremberg, Germany).





^ permalink raw reply	[flat|nested] 12+ messages in thread

* bug#37910: CC Mode 5.33.2 (C++//l); CC-mode inconsistently indents everything as topmost-intro after a while
  2019-11-10 17:47     ` Eric Scrivner
  2019-11-10 17:50       ` Eric Scrivner
  2019-11-10 19:02       ` Alan Mackenzie
@ 2019-11-13 18:38       ` Alan Mackenzie
  2019-11-14  0:12         ` Eric Scrivner
  2 siblings, 1 reply; 12+ messages in thread
From: Alan Mackenzie @ 2019-11-13 18:38 UTC (permalink / raw)
  To: Eric Scrivner; +Cc: 37910

Hello again, Eric.

On Sun, Nov 10, 2019 at 09:47:35 -0800, Eric Scrivner wrote:
> I've applied the patch and run emacs with it. Unfortunately, it doesn't
> seem to solve the issue. One the bright side, I believe I had buffers which
> can reliably reproduce the issue (at least locally). I've copied one such
> file below. The issue can be reproduced by simply going to the end of
> `main` and attempting to indent the `SQL_Quit()` statement. At first it
> attempts to overindent the line as `statement-cont`, then if I go up and
> attempt to indent the `PlatformLog` in the else statement then return to
> the SDL_Quit and indent it, it has become `topmost-intro`. Apologies for
> the long length of the file, but hopefully it can help with reproduction:

I've had a look at the bug and there was a non-sensical function in CC
Mode fouling things up.  In the patch below, I've rewritten this
function.  Could I ask you please to apply the new patch (to the
original Emacs 26.3 version off cc-engine.el), byte-compile it and test
it.  Then please let me know how it goes.  However ....

I would strongly advise you to set the Emacs variable
open-parens-in-column-0-is-defun-start to nil.  Whether for C++ Mode in
general (in a hook function), for this one file in particular (in a
Local Variables: section at the end of the file), or globally.

With open-p......-start left at its default of t, Emacs will search
backward for the beginning of a function by searching for an open
paren/brace/bracket at column 0.  It deems the found paren/brace/bracket
the start of a function even if that p/b/b is inside a string or
function.  This seems likely to happen in your test file, where you've
got such braces at column 0 inside two raw strings.

This variable codifies a difficult dilemma for Emacs.  Without the paren
in column 0 heuristic, the beginning of function search would have to
scan to the beginning of the buffer every time which is somewhat slow
(perhaps less slow now on modern PCs, but still slow).  With the
heuristic, analysis problems happen when there are braces at col 0 which
aren't BO functions (e.g., in C++ Mode when things inside a namespace
start at column 0, a popular convention).  For a time, o-p-i-c-0-i-d-s
was spiked to nil by CC Mode, but this led to complaints of slowness.

Anyhow, here's the improved patch.  I look forward to hearing back from
you.  Thanks for all the time you've spent on this bug.



diff -r 2783baa48d44 cc-engine.el
--- a/cc-engine.el	Fri Oct 25 20:00:14 2019 +0000
+++ b/cc-engine.el	Wed Nov 13 18:13:11 2019 +0000
@@ -3371,19 +3371,35 @@
   (or (car (c-state-literal-at pos))
       pos))
 
-(defsubst c-state-cache-non-literal-place (pos state)
-  ;; Return a position outside of a string/comment/macro at or before POS.
-  ;; STATE is the parse-partial-sexp state at POS.
-  (let ((res (if (or (nth 3 state)	; in a string?
-		     (and (nth 4 state)
-			  (not (eq (nth 7 state) 'syntax-table)))) ; in a comment?
-		 (nth 8 state)
-	       pos)))
+(defun c-state-cache-lower-good-pos (here pos state)
+  ;; Return a good pos (in the sense of `c-state-cache-good-pos') at the
+  ;; lowest[*] position between POS and HERE which is syntactically equivalent
+  ;; to HERE.  This position may be HERE itself.  POS is before HERE in the
+  ;; buffer.
+  ;; [*] We don't actually always determine this exact position, since this
+  ;; would require a disproportionate amount of work, given that this function
+  ;; deals only with a corner condition, and POS and HERE are typically on
+  ;; adjacent lines.  We actually return either POS, when POS is a good
+  ;; position, HERE otherwise.  Exceptionally, when POS is in a comment, but
+  ;; HERE not, we can return the position of the end of the comment.
+  (let (s)
     (save-excursion
-      (goto-char res)
-      (if (c-beginning-of-macro)
-	  (point)
-	res))))
+      (goto-char pos)
+      (when (nth 8 state)	; POS in a comment or string.  Move out of it.
+	(setq s (parse-partial-sexp pos here nil nil state 'syntax-table))
+	(when (< (point) here)
+	  (setq pos (point)
+		state s)))
+      (if (eq (point) here)		; HERE is in the same literal as POS
+	  pos
+	(setq s (parse-partial-sexp pos here (1+ (car state)) nil state nil))
+	(cond
+	 ((> (car s) (car state))  ; Moved into a paren between POS and HERE
+	  here)
+	 ((not (eq (nth 6 s) (car state))) ; Moved out of a paren between POS
+					; and HERE
+	  here)
+	 (t pos))))))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; Stuff to do with point-min, and coping with any literal there.
@@ -3690,7 +3706,13 @@
 							; brace pair.
 	    (setq c-state-cache nil
 		  c-state-cache-good-pos c-state-min-scan-pos)
-	  (setcdr ptr nil)
+	  ;; Do not alter the original `c-state-cache' structure, since there
+	  ;; may be a loop suspended which is looping through that structure.
+	  ;; This may have been the cause of bug #37910.
+	  (let ((cdr-ptr (cdr ptr)))
+	    (setcdr ptr nil)
+	    (setq c-state-cache (copy-sequence c-state-cache))
+	    (setcdr ptr cdr-ptr))
 	  (setq c-state-cache-good-pos (1+ (c-state-cache-top-lparen))))
 	)))
 
@@ -3793,11 +3815,12 @@
 		(setq new-cons (cons bra (1+ ce)))
 		(cond
 		 ((consp (car c-state-cache))
-		  (setcar c-state-cache new-cons))
+		  (setq c-state-cache (cons new-cons (cdr c-state-cache))))
 		 ((and (numberp (car c-state-cache)) ; probably never happens
 		       (< ce (car c-state-cache)))
-		  (setcdr c-state-cache
-			  (cons new-cons (cdr c-state-cache))))
+		  (setq c-state-cache
+			(cons (car c-state-cache)
+			      (cons new-cons (cdr c-state-cache)))))
 		 (t (setq c-state-cache (cons new-cons c-state-cache)))))
 
 	    ;; We haven't found a brace pair.  Record this in the cache.
@@ -3998,7 +4021,7 @@
 	(when (and c-state-cache
 		   (consp (car c-state-cache))
 		   (> (cdar c-state-cache) upper-lim))
-	  (setcar c-state-cache (caar c-state-cache))
+	  (setq c-state-cache (cons (caar c-state-cache) (cdr c-state-cache)))
 	  (setq scan-back-pos (car c-state-cache)
 		cons-separated t))
 
@@ -4135,7 +4158,7 @@
       ;; knowledge of what's inside these braces, we have no alternative but
       ;; to direct the caller to scan the buffer from the opening brace.
       (setq pos (caar c-state-cache))
-      (setcar c-state-cache pos)
+      (setq c-state-cache (cons pos (cdr c-state-cache)))
       (list (1+ pos) pos t)) ; return value.  We've just converted a brace pair
 			     ; entry into a { entry, so the caller needs to
 			     ; search for a brace pair before the {.
@@ -4385,7 +4408,7 @@
       (setq c-state-cache-good-pos
 	    (if (and bopl-state
 		     (< good-pos (- here c-state-cache-too-far)))
-		(c-state-cache-non-literal-place here-bopl bopl-state)
+		(c-state-cache-lower-good-pos here here-bopl bopl-state)
 	      good-pos)))
 
      ((eq strategy 'backward)


-- 
Alan Mackenzie (Nuremberg, Germany).





^ permalink raw reply	[flat|nested] 12+ messages in thread

* bug#37910: CC Mode 5.33.2 (C++//l); CC-mode inconsistently indents everything as topmost-intro after a while
  2019-11-13 18:38       ` Alan Mackenzie
@ 2019-11-14  0:12         ` Eric Scrivner
  2019-11-14 20:11           ` bug#5490: " Alan Mackenzie
  0 siblings, 1 reply; 12+ messages in thread
From: Eric Scrivner @ 2019-11-14  0:12 UTC (permalink / raw)
  To: Alan Mackenzie; +Cc: 37910

[-- Attachment #1: Type: text/plain, Size: 8469 bytes --]

Hi Andrew,

Thanks so much for the patch and explanation. I applied it locally and
while the patch didn't seem to have much effect, setting
`open-paren-in-column-0-is-defun-start` to `nil` seems to have done the
trick here without any noticeable impact on performance. It seems as you've
said that there may be some issues in the heuristic used for performance
optimization here.

I will let you know if any other issues surface, but it appears this has
fixed my issue.

On Wed, Nov 13, 2019 at 10:39 AM Alan Mackenzie <acm@muc.de> wrote:

> Hello again, Eric.
>
> On Sun, Nov 10, 2019 at 09:47:35 -0800, Eric Scrivner wrote:
> > I've applied the patch and run emacs with it. Unfortunately, it doesn't
> > seem to solve the issue. One the bright side, I believe I had buffers
> which
> > can reliably reproduce the issue (at least locally). I've copied one such
> > file below. The issue can be reproduced by simply going to the end of
> > `main` and attempting to indent the `SQL_Quit()` statement. At first it
> > attempts to overindent the line as `statement-cont`, then if I go up and
> > attempt to indent the `PlatformLog` in the else statement then return to
> > the SDL_Quit and indent it, it has become `topmost-intro`. Apologies for
> > the long length of the file, but hopefully it can help with reproduction:
>
> I've had a look at the bug and there was a non-sensical function in CC
> Mode fouling things up.  In the patch below, I've rewritten this
> function.  Could I ask you please to apply the new patch (to the
> original Emacs 26.3 version off cc-engine.el), byte-compile it and test
> it.  Then please let me know how it goes.  However ....
>
> I would strongly advise you to set the Emacs variable
> open-parens-in-column-0-is-defun-start to nil.  Whether for C++ Mode in
> general (in a hook function), for this one file in particular (in a
> Local Variables: section at the end of the file), or globally.
>
> With open-p......-start left at its default of t, Emacs will search
> backward for the beginning of a function by searching for an open
> paren/brace/bracket at column 0.  It deems the found paren/brace/bracket
> the start of a function even if that p/b/b is inside a string or
> function.  This seems likely to happen in your test file, where you've
> got such braces at column 0 inside two raw strings.
>
> This variable codifies a difficult dilemma for Emacs.  Without the paren
> in column 0 heuristic, the beginning of function search would have to
> scan to the beginning of the buffer every time which is somewhat slow
> (perhaps less slow now on modern PCs, but still slow).  With the
> heuristic, analysis problems happen when there are braces at col 0 which
> aren't BO functions (e.g., in C++ Mode when things inside a namespace
> start at column 0, a popular convention).  For a time, o-p-i-c-0-i-d-s
> was spiked to nil by CC Mode, but this led to complaints of slowness.
>
> Anyhow, here's the improved patch.  I look forward to hearing back from
> you.  Thanks for all the time you've spent on this bug.
>
>
>
> diff -r 2783baa48d44 cc-engine.el
> --- a/cc-engine.el      Fri Oct 25 20:00:14 2019 +0000
> +++ b/cc-engine.el      Wed Nov 13 18:13:11 2019 +0000
> @@ -3371,19 +3371,35 @@
>    (or (car (c-state-literal-at pos))
>        pos))
>
> -(defsubst c-state-cache-non-literal-place (pos state)
> -  ;; Return a position outside of a string/comment/macro at or before POS.
> -  ;; STATE is the parse-partial-sexp state at POS.
> -  (let ((res (if (or (nth 3 state)     ; in a string?
> -                    (and (nth 4 state)
> -                         (not (eq (nth 7 state) 'syntax-table)))) ; in a
> comment?
> -                (nth 8 state)
> -              pos)))
> +(defun c-state-cache-lower-good-pos (here pos state)
> +  ;; Return a good pos (in the sense of `c-state-cache-good-pos') at the
> +  ;; lowest[*] position between POS and HERE which is syntactically
> equivalent
> +  ;; to HERE.  This position may be HERE itself.  POS is before HERE in
> the
> +  ;; buffer.
> +  ;; [*] We don't actually always determine this exact position, since
> this
> +  ;; would require a disproportionate amount of work, given that this
> function
> +  ;; deals only with a corner condition, and POS and HERE are typically on
> +  ;; adjacent lines.  We actually return either POS, when POS is a good
> +  ;; position, HERE otherwise.  Exceptionally, when POS is in a comment,
> but
> +  ;; HERE not, we can return the position of the end of the comment.
> +  (let (s)
>      (save-excursion
> -      (goto-char res)
> -      (if (c-beginning-of-macro)
> -         (point)
> -       res))))
> +      (goto-char pos)
> +      (when (nth 8 state)      ; POS in a comment or string.  Move out of
> it.
> +       (setq s (parse-partial-sexp pos here nil nil state 'syntax-table))
> +       (when (< (point) here)
> +         (setq pos (point)
> +               state s)))
> +      (if (eq (point) here)            ; HERE is in the same literal as
> POS
> +         pos
> +       (setq s (parse-partial-sexp pos here (1+ (car state)) nil state
> nil))
> +       (cond
> +        ((> (car s) (car state))  ; Moved into a paren between POS and
> HERE
> +         here)
> +        ((not (eq (nth 6 s) (car state))) ; Moved out of a paren between
> POS
> +                                       ; and HERE
> +         here)
> +        (t pos))))))
>
>  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
>  ;; Stuff to do with point-min, and coping with any literal there.
> @@ -3690,7 +3706,13 @@
>                                                         ; brace pair.
>             (setq c-state-cache nil
>                   c-state-cache-good-pos c-state-min-scan-pos)
> -         (setcdr ptr nil)
> +         ;; Do not alter the original `c-state-cache' structure, since
> there
> +         ;; may be a loop suspended which is looping through that
> structure.
> +         ;; This may have been the cause of bug #37910.
> +         (let ((cdr-ptr (cdr ptr)))
> +           (setcdr ptr nil)
> +           (setq c-state-cache (copy-sequence c-state-cache))
> +           (setcdr ptr cdr-ptr))
>           (setq c-state-cache-good-pos (1+ (c-state-cache-top-lparen))))
>         )))
>
> @@ -3793,11 +3815,12 @@
>                 (setq new-cons (cons bra (1+ ce)))
>                 (cond
>                  ((consp (car c-state-cache))
> -                 (setcar c-state-cache new-cons))
> +                 (setq c-state-cache (cons new-cons (cdr c-state-cache))))
>                  ((and (numberp (car c-state-cache)) ; probably never
> happens
>                        (< ce (car c-state-cache)))
> -                 (setcdr c-state-cache
> -                         (cons new-cons (cdr c-state-cache))))
> +                 (setq c-state-cache
> +                       (cons (car c-state-cache)
> +                             (cons new-cons (cdr c-state-cache)))))
>                  (t (setq c-state-cache (cons new-cons c-state-cache)))))
>
>             ;; We haven't found a brace pair.  Record this in the cache.
> @@ -3998,7 +4021,7 @@
>         (when (and c-state-cache
>                    (consp (car c-state-cache))
>                    (> (cdar c-state-cache) upper-lim))
> -         (setcar c-state-cache (caar c-state-cache))
> +         (setq c-state-cache (cons (caar c-state-cache) (cdr
> c-state-cache)))
>           (setq scan-back-pos (car c-state-cache)
>                 cons-separated t))
>
> @@ -4135,7 +4158,7 @@
>        ;; knowledge of what's inside these braces, we have no alternative
> but
>        ;; to direct the caller to scan the buffer from the opening brace.
>        (setq pos (caar c-state-cache))
> -      (setcar c-state-cache pos)
> +      (setq c-state-cache (cons pos (cdr c-state-cache)))
>        (list (1+ pos) pos t)) ; return value.  We've just converted a
> brace pair
>                              ; entry into a { entry, so the caller needs to
>                              ; search for a brace pair before the {.
> @@ -4385,7 +4408,7 @@
>        (setq c-state-cache-good-pos
>             (if (and bopl-state
>                      (< good-pos (- here c-state-cache-too-far)))
> -               (c-state-cache-non-literal-place here-bopl bopl-state)
> +               (c-state-cache-lower-good-pos here here-bopl bopl-state)
>               good-pos)))
>
>       ((eq strategy 'backward)
>
>
> --
> Alan Mackenzie (Nuremberg, Germany).
>

[-- Attachment #2: Type: text/html, Size: 9957 bytes --]

^ permalink raw reply	[flat|nested] 12+ messages in thread

* bug#5490: bug#37910: CC Mode 5.33.2 (C++//l); CC-mode inconsistently indents everything as topmost-intro after a while
  2019-11-14  0:12         ` Eric Scrivner
@ 2019-11-14 20:11           ` Alan Mackenzie
  2019-11-15  3:11             ` bug#18072: " Stefan Kangas
  2020-08-12 18:50             ` Stefan Kangas
  0 siblings, 2 replies; 12+ messages in thread
From: Alan Mackenzie @ 2019-11-14 20:11 UTC (permalink / raw)
  To: Eric Scrivner; +Cc: 37910-done, Stefan Kangas, 5490, 18072

Hello, Eric.

On Wed, Nov 13, 2019 at 16:12:11 -0800, Eric Scrivner wrote:
> Hi Alan,

> Thanks so much for the patch and explanation. I applied it locally and
> while the patch didn't seem to have much effect, setting
> `open-paren-in-column-0-is-defun-start` to `nil` seems to have done the
> trick here without any noticeable impact on performance. It seems as you've
> said that there may be some issues in the heuristic used for performance
> optimization here.

> I will let you know if any other issues surface, but it appears this has
> fixed my issue.

OK, thanks for the test.  I think it is now time to commit the patch and
close the bug.

I am hoping that the patch, already applied to the savannah master
branch, will also have fixed bugs #5910 and #18072.  Their (sporadic)
symptoms were consistent with the glitches fixed by the patch.

-- 
Alan Mackenzie (Nuremberg, Germany).





^ permalink raw reply	[flat|nested] 12+ messages in thread

* bug#18072: bug#37910: CC Mode 5.33.2 (C++//l); CC-mode inconsistently indents everything as topmost-intro after a while
  2019-11-14 20:11           ` bug#5490: " Alan Mackenzie
@ 2019-11-15  3:11             ` Stefan Kangas
  2020-08-12 18:50             ` Stefan Kangas
  1 sibling, 0 replies; 12+ messages in thread
From: Stefan Kangas @ 2019-11-15  3:11 UTC (permalink / raw)
  To: Alan Mackenzie; +Cc: Juhani Åhman, 18072

Hi Juhani,

Alan Mackenzie <acm@muc.de> writes:

> On Wed, Nov 13, 2019 at 16:12:11 -0800, Eric Scrivner wrote:
>> Hi Alan,
>
>> Thanks so much for the patch and explanation. I applied it locally and
>> while the patch didn't seem to have much effect, setting
>> `open-paren-in-column-0-is-defun-start` to `nil` seems to have done the
>> trick here without any noticeable impact on performance. It seems as you've
>> said that there may be some issues in the heuristic used for performance
>> optimization here.
>
>> I will let you know if any other issues surface, but it appears this has
>> fixed my issue.
>
> OK, thanks for the test.  I think it is now time to commit the patch and
> close the bug.
>
> I am hoping that the patch, already applied to the savannah master
> branch, will also have fixed bugs #5910 and #18072.  Their (sporadic)
> symptoms were consistent with the glitches fixed by the patch.

I'm not sure if you're still in a position to reproduce Bug#18072.  If
you are, could you please help test Alan's patch?

Thanks in advance.

Best regards,
Stefan Kangas





^ permalink raw reply	[flat|nested] 12+ messages in thread

* bug#18072: bug#37910: CC Mode 5.33.2 (C++//l); CC-mode inconsistently indents everything as topmost-intro after a while
  2019-11-14 20:11           ` bug#5490: " Alan Mackenzie
  2019-11-15  3:11             ` bug#18072: " Stefan Kangas
@ 2020-08-12 18:50             ` Stefan Kangas
  2020-08-13 18:35               ` Alan Mackenzie
  1 sibling, 1 reply; 12+ messages in thread
From: Stefan Kangas @ 2020-08-12 18:50 UTC (permalink / raw)
  To: Alan Mackenzie; +Cc: 18072, 37910-done, 5490, Eric Scrivner

Hi Alan,

Alan Mackenzie <acm@muc.de> writes:

> OK, thanks for the test.  I think it is now time to commit the patch and
> close the bug.
>
> I am hoping that the patch, already applied to the savannah master
> branch, will also have fixed bugs #5910 and #18072.  Their (sporadic)
> symptoms were consistent with the glitches fixed by the patch.

Just to follow up on this.

If this was committed, is it time to also close Bug#18072?

Best regards,
Stefan Kangas





^ permalink raw reply	[flat|nested] 12+ messages in thread

* bug#18072: bug#37910: CC Mode 5.33.2 (C++//l); CC-mode inconsistently indents everything as topmost-intro after a while
  2020-08-12 18:50             ` Stefan Kangas
@ 2020-08-13 18:35               ` Alan Mackenzie
  2020-08-13 19:50                 ` Stefan Kangas
  0 siblings, 1 reply; 12+ messages in thread
From: Alan Mackenzie @ 2020-08-13 18:35 UTC (permalink / raw)
  To: Stefan Kangas; +Cc: 18072, 37910, 5490, Eric Scrivner

Hello, Stefan.

On Wed, Aug 12, 2020 at 11:50:21 -0700, Stefan Kangas wrote:
> Hi Alan,

> Alan Mackenzie <acm@muc.de> writes:

> > OK, thanks for the test.  I think it is now time to commit the patch
> > and close the bug.

> > I am hoping that the patch, already applied to the savannah master
> > branch, will also have fixed bugs #5910 and #18072.  Their (sporadic)
> > symptoms were consistent with the glitches fixed by the patch.

> Just to follow up on this.

> If this was committed, is it time to also close Bug#18072?

I had a wee look at it last night, and honestly can't see any good from
keeping it open.  It's almost certainly fixed, but it was a bug without a
reproducible test case.

So, I say close it!

> Best regards,
> Stefan Kangas

-- 
Alan Mackenzie (Nuremberg, Germany).





^ permalink raw reply	[flat|nested] 12+ messages in thread

* bug#18072: bug#37910: CC Mode 5.33.2 (C++//l); CC-mode inconsistently indents everything as topmost-intro after a while
  2020-08-13 18:35               ` Alan Mackenzie
@ 2020-08-13 19:50                 ` Stefan Kangas
  0 siblings, 0 replies; 12+ messages in thread
From: Stefan Kangas @ 2020-08-13 19:50 UTC (permalink / raw)
  To: Alan Mackenzie; +Cc: Eric Scrivner, 18072-done

Hi Alan,

Alan Mackenzie <acm@muc.de> writes:

> I had a wee look at it last night, and honestly can't see any good from
> keeping it open.  It's almost certainly fixed, but it was a bug without a
> reproducible test case.
>
> So, I say close it!

Excellent, thanks for looking into it.  I'm closing the bug now.

Best regards,
Stefan Kangas





^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2020-08-13 19:50 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <CANJXwL=WHfvFSAHxxNQ=92_R193gFPU7Hu7zwXCCEO3Z0Nyn3A@mail.gmail.com>
2019-10-27 15:39 ` bug#37910: CC Mode 5.33.2 (C++//l); CC-mode inconsistently indents everything as topmost-intro after a while Alan Mackenzie
2019-11-10 10:48   ` Alan Mackenzie
2019-11-10 17:47     ` Eric Scrivner
2019-11-10 17:50       ` Eric Scrivner
2019-11-10 19:02       ` Alan Mackenzie
2019-11-13 18:38       ` Alan Mackenzie
2019-11-14  0:12         ` Eric Scrivner
2019-11-14 20:11           ` bug#5490: " Alan Mackenzie
2019-11-15  3:11             ` bug#18072: " Stefan Kangas
2020-08-12 18:50             ` Stefan Kangas
2020-08-13 18:35               ` Alan Mackenzie
2020-08-13 19:50                 ` Stefan Kangas

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).