Subject: Re: bug#37910: CC Mode 5.33.2 (C++//l); CC-mode inconsistently indents everything as topmost-intro after a while

From: Eric Scrivner <[email-masked]>
Date: Sun, 10 Nov 2019 09:50:01 -0800

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 <[email-masked]>
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 <[email-masked]> 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).
>>
>