From: Eric Scrivner <eric.t.scrivner@gmail.com>
To: Alan Mackenzie <acm@muc.de>
Cc: 37910@debbugs.gnu.org
Subject: bug#37910: CC Mode 5.33.2 (C++//l); CC-mode inconsistently indents everything as topmost-intro after a while
Date: Sun, 10 Nov 2019 09:47:35 -0800 [thread overview]
Message-ID: <CANJXwLnYXJ1+G56rTvJ5KsrRLOQpxRvOtqD-HHhg_R47a9CrNA@mail.gmail.com> (raw)
In-Reply-To: <20191110104811.GA6614@ACM>
[-- 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 --]
next prev parent reply other threads:[~2019-11-10 17:47 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
[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 [this message]
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
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://www.gnu.org/software/emacs/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=CANJXwLnYXJ1+G56rTvJ5KsrRLOQpxRvOtqD-HHhg_R47a9CrNA@mail.gmail.com \
--to=eric.t.scrivner@gmail.com \
--cc=37910@debbugs.gnu.org \
--cc=acm@muc.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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).