Мнимая неадекватная работа компилятора

Я решил написать змейку и заметил, что компилятор иногда игнорирует очевидные ошибки. Тогда мой код компилируется, но дальше белого окна не работает. Версия 3.8.2, сборка 3063. Мой код:

uses GraphAbc, Timers;
const SCREEN_WIDTH = 640;
const SCREEN_HEIGHT = 480;
const MAX_FPS = 60;

const WORLD_WIDTH = 48;
const WORLD_HEIGHT = 36;

const START_CELLS_AMOUNT = 4;
const FOOD_AMOUNT = 6;

const SKIP_STEPS = true;

const BACKGROUND = clDarkGray;
const SNAKE_COLOR = clGray;
const FOOD_COLOR = RGB(20, 70, 180);


var snakeX: array of integer;
var snakeY: array of integer;
var dir: integer;
var foodX: array of integer;
var foodY: array of integer;
var timer: Timers.Timer;

function generateFood(): (integer, integer);
begin
  var x: integer;
  var y: integer;
  while true do
  begin
    x := Random(WORLD_WIDTH);
    y := Random(WORLD_HEIGHT);
    var cont := false;
    for var i := 0 to snakeX.Length - 1 do
    begin
      if (snakeX[i] = x) and (snakeY[i] = y) then
      begin
        cont := true;
        break;
      end;
    end;
    if cont then continue;
    for var i := 0 to FOOD_AMOUNT - 1 do
    begin
      if (foodX[i] = x) and (foodY[i] = y) then
      begin
        cont := true;
        continue;
      end;
    end;
    if not cont then break;
  end;
  generateFood := (x, y);
end;

procedure KeyDown(Key: integer);
begin
  case Key of
    VK_Up: dir := 0;
    VK_Right: dir := 1;
    VK_Down: dir := 2;
    VK_Left: dir := 3;
  end;
end;

procedure PaintCell(coords: (integer, integer); col: color);
begin
  SetBrushColor(col);
  FillRectangle(
  Ceil(coords[0] * SCREEN_WIDTH / WORLD_WIDTH),
  Ceil(coords[1] * SCREEN_HEIGHT / WORLD_HEIGHT),
  Ceil((coords[0] + 1) * SCREEN_WIDTH / WORLD_WIDTH),
  Ceil((coords[1] + 1) * SCREEN_HEIGHT / WORLD_HEIGHT));
end;

procedure Draw();
begin
  ClearWindow(BACKGROUND);
  for var i := 0 to snakeX.Length - 1 do PaintCell((snakeX[i], snakeY[i]), SNAKE_COLOR);
  for var i := 0 to foodX.Length - 1 do PaintCell((foodX[i], foodY[i]), FOOD_COLOR);
  Redraw;
end;

procedure Update;
begin
  var tempDirs := dir;
  var xSpeed := 0;
  var ySpeed := 0;
  case tempDirs of
    0: ySpeed := -1;
    1: xSpeed := 1;
    2: ySpeed := 1;
    3: xSpeed := -1;
  end;
  
  snakeX := snakeX[1:snakeX.Length - 1] + new integer[1](snakeX[snakeX.Length - 1] + xSpeed);
  snakeY := snakeY[1:snakeX.Length - 1] + new integer[1](snakeY[snakeY.Length - 1] + ySpeed);
  
  Draw();
end;

procedure Init;
begin
  setLength(snakeX, START_CELLS_AMOUNT);
  setLength(snakeY, START_CELLS_AMOUNT);
  setLength(foodX, FOOD_AMOUNT);
  setLength(foodY, FOOD_AMOUNT);
  dir := 1;
  SetConsoleIO;
  LockDrawing;
  SetWindowSize(SCREEN_WIDTH, SCREEN_HEIGHT);
  SetSmoothingOn;
  for var i := 0 to START_CELLS_AMOUNT - 1 do
  begin
    snakeX[i] := Round(WORLD_WIDTH / 2) - i;
    snakeY[i] := Round(WORLD_HEIGHT / 2);
  end;

  for var i := 0 to FOOD_AMOUNT - 1 do
  begin
    foodX[i] := -1;
    foodY[i] := -1;
  end;
  for var i := 0 to FOOD_AMOUNT - 1 do (foodX[i], foodY[i]) := generateFood;
  OnKeyDown := KeyDown;
end;

procedure MainLoop;
begin
  timer := new Timers.Timer(1000, Update);
  timer.Start();
end;

begin
  Init();
  MainLoop();
end.

Даже на этой же сборке ловлю ошибку компиляции

Program1.pas(16) : Ожидалось константное выражение

Уточните, что конкретно игнорирует компилятор? Так же, обычно любые ошибки компилятора принято демонстрировать на минимальном коде. Естественно, что до 10-15 строк свести ошибку не всегда выходит, но из этого кода, я думаю, можно было бы много чего выкинуть

Это я оставил как пример игнорируемой ошибки. При этом иногда он мне всё-же пишет о ней.

я не понимаю, что Вы хотите. что бы писал или что бы не писал?..

Если он не пишет, то программа без каких-либо соответствующих сообщений зависает. Так что хочу, что-бы писал.

Оказалось, что всё завистет на этом срезе: snakeX[1:snakeX.Length - 1]. Почему-то он не выводил, что ему тут что-то не нравится.

Не совсем понятно как срез с неверным использованием константы тут связан, если честно. Вообще мало данных, что бы понять в каком случае игнорируется неверная константа. Сколько раз не запускал, а ошибку всегда выдаёт

1 лайк

У вас странное понимание компилятора. Компилятор это то что делает бинарный исполняемый файл из текстового файла с кодом. Он не используется для выполнения кода.

Если вы пытаетесь получить элемент за границей массива - компилятор вам ничего не должен говорить.
О том что операция недопустима станет понятно только в процессе выполнения программы. И обычно так и пишет - ошибка времени выполнения.

Точнее, ошибки времени выполнения это исключения. Читайте как работает try в справке.
Так вот, подпрограмма которую вы посылаете в конструктор Timer, считайте, вызывается как try Update; except end;. Потому что ошибке из таймера некуда деться - код с этой ошибкой выполняется в отдельном потоке выполнения.
Хотя вообще поидее это должно крашить всю программу. И малакасофт уже больше десятиления грозятся это исправить:

The Timer component catches and suppresses all exceptions thrown by event handlers for the Elapsed event. This behavior is subject to change in future releases of the .NET Framework.

И @samurai правильно сказал, вы запостили очень не аккуратный вопрос и ждёте что мы навангуем всё что должны были сделать вы, включая нахождение минимального кода.
На будущее, когда что то идёт не так - делаете копию вашего проекта, и начинаете удалять из копии куски кода, так чтобы то что вам не нравится продолжало проявлятся. Пока не найдёте самый минимум кода, с неправильным поведением.

Обычно все вопросы решаются сами в процессе этого действия. Но если показать найденный минимальный код - можно получить ещё больше знаний в виде наставлений от тех кто с такой же проблемой сталкивался.
А пока вы кидаете стенку кода с кучей воды (относительно проблемы) - никому не захочется и смотреть на него. Я делаю исключение только потому что сильно добрый сейчас.

1 лайк

Класно сказал, я поддерживаю! :grinning:

В .NET 6 наверно исправили