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 #include #include #include 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 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 > ...... > > , and 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). >