Замечания и предложения

Это чья идея - Ваша? Основная ОС для .NET - это Windows и в ней по умолчанию используется UTF-16. Кроме того, в PacscalАВС.NЕТ изначально декларировалась совместимость с Delphi.

Ошибка идёт в вывод вместо списка ошибок:

## var a:=Arr(1);
Write(a[a.GetLowerBound]);

Тут надо начать с того, что это ошибка среды выполнения, как эта, а не куда её выводит.
Эта ошибка значит что компилятор сгенерировал не_выполняемый .exe файл. Такое сразу в новую issue.

В следующем обновлении консоль будет юникодной

2 лайка

Тема: идентификаторы.

## var a:=System.Globalization.UnicodeCategory.Format;

Программа не компилируется. При нажатии на кнопку “Сохранить отчёт как…” диалогового окна “Внутренняя ошибка компилятора”, оно бесконечно “Не отвечает”. Если открыть 2 таких окна, IDE бесконечно “Не отвечает”.

Кажется, ОК из-за того, что идентификаторы являются ключами, следовательно, не могут совпадать, а это происходит. Одинаковое имя имеют и константа перечисления, и функция Enum.Format.

Наверняка этот случай не единичный. Подпрограммы с аргументами можно отличать по наличию аргументов, а как — остальные идентификаторы? В таких случаях придётся терять совместимость со старыми версиями Pascal (в данном случае переименовать Enum.Format), поскольку потерять её с Microsoft.NET (в данном случае переименовать UnicodeCategory.Format) невозможно?

Отчёт
09.08.2021 3:06:11
PascalABCCompiler.Core v3.8.0.2949 (01.08.2021), debug version
Runtime version: 4.0.30319.42000
OS version: Microsoft Windows NT 10.0.19042.0
Processor count: 8
WorkingSet: 114360 kb
StatesList: 
BeginCompileFile Program1.pas
BeginParsingFile Program1.pas
EndParsingFile Program1.pas
SyntaxTreeConversion Standard
ReadPCUFile PABCSystem.pcu
ReadDLL System.dll
ReadDLL mscorlib.dll
ReadDLL System.Core.dll
ReadDLL System.Numerics.dll
ReadPCUFile PABCExtensions.pcu
ReadPCUFile __RunMode.pcu
CompileInterface Program1.pas
CompilationFinished Program1.pas
Ready

Error[0]: Внутренняя ошибка компилятора в модуле [pabcnetc.exe] :'System.Exception: System.NullReferenceException: Ссылка на объект не указывает на экземпляр объекта.
   в PascalABCCompiler.TreeConverter.convertion_data_and_alghoritms.function_eq_generic_params(function_node left, function_node right)
   в PascalABCCompiler.TreeConverter.convertion_data_and_alghoritms.function_eq_params(function_node left, function_node right, Boolean compare_parameter_types)
   в PascalABCCompiler.TreeConverter.convertion_data_and_alghoritms.find_eq_method_in_list(function_node fn, List`1 funcs)
   в PascalABCCompiler.TreeConverter.syntax_tree_visitor.create_possible_delegates_list(expression_node obj, List`1 sil, location loc, Boolean is_static)
   в PascalABCCompiler.TreeConverter.syntax_tree_visitor.make_delegate_wrapper(expression_node obj, List`1 sil, location loc, Boolean is_static)
   в PascalABCCompiler.TreeConverter.syntax_tree_visitor.create_static_expression(type_node tn, ident id_right, List`1 si_right)
   в PascalABCCompiler.TreeConverter.syntax_tree_visitor.dot_node_as_type_ident(type_node tn, ident id_right, motivation mot)
   в PascalABCCompiler.TreeConverter.returner.visit(expression expr)
   в PascalABCCompiler.TreeConverter.syntax_tree_visitor.convert_strong(expression expr)
   в PascalABCCompiler.TreeConverter.syntax_tree_visitor.visit(var_def_statement _var_def_statement)
   в PascalABCCompiler.TreeConverter.syntax_tree_visitor.visit(var_statement node)
   в PascalABCCompiler.TreeConverter.syntax_tree_visitor.convert_strong(statement st)
   в PascalABCCompiler.TreeConverter.syntax_tree_visitor.visit(statement_list _statement_list)
   в PascalABCCompiler.TreeConverter.syntax_tree_visitor.convert_strong(statement st)
   в PascalABCCompiler.TreeConverter.syntax_tree_visitor.visit_program_code(statement_list program_code)
   в PascalABCCompiler.TreeConverter.syntax_tree_visitor.visit(block _block)
   в PascalABCCompiler.TreeConverter.syntax_tree_visitor.visit(program_module _program_module)
   в PascalABCCompiler.TreeConverter.SyntaxTreeToSemanticTreeConverter.CompileInterface(compilation_unit SyntaxUnit, unit_node_list UsedUnits, List`1 ErrorsList, List`1 WarningsList, SyntaxError parser_error, Hashtable bad_nodes, using_namespace_list namespaces, Dictionary`2 docs, Boolean debug, Boolean debugging)
   в PascalABCCompiler.Compiler.CompileUnit(unit_node_list Units, Dictionary`2 DirectCompilationUnits, unit_or_namespace SyntaxUsesUnit, String prev_path)
   в PascalABCCompiler.Compiler.Compile() System.Exception: System.NullReferenceException: Ссылка на объект не указывает на экземпляр объекта.
   в PascalABCCompiler.TreeConverter.convertion_data_and_alghoritms.function_eq_generic_params(function_node left, function_node right)
   в PascalABCCompiler.TreeConverter.convertion_data_and_alghoritms.function_eq_params(function_node left, function_node right, Boolean compare_parameter_types)
   в PascalABCCompiler.TreeConverter.convertion_data_and_alghoritms.find_eq_method_in_list(function_node fn, List`1 funcs)
   в PascalABCCompiler.TreeConverter.syntax_tree_visitor.create_possible_delegates_list(expression_node obj, List`1 sil, location loc, Boolean is_static)
   в PascalABCCompiler.TreeConverter.syntax_tree_visitor.make_delegate_wrapper(expression_node obj, List`1 sil, location loc, Boolean is_static)
   в PascalABCCompiler.TreeConverter.syntax_tree_visitor.create_static_expression(type_node tn, ident id_right, List`1 si_right)
   в PascalABCCompiler.TreeConverter.syntax_tree_visitor.dot_node_as_type_ident(type_node tn, ident id_right, motivation mot)
   в PascalABCCompiler.TreeConverter.returner.visit(expression expr)
   в PascalABCCompiler.TreeConverter.syntax_tree_visitor.convert_strong(expression expr)
   в PascalABCCompiler.TreeConverter.syntax_tree_visitor.visit(var_def_statement _var_def_statement)
   в PascalABCCompiler.TreeConverter.syntax_tree_visitor.visit(var_statement node)
   в PascalABCCompiler.TreeConverter.syntax_tree_visitor.convert_strong(statement st)
   в PascalABCCompiler.TreeConverter.syntax_tree_visitor.visit(statement_list _statement_list)
   в PascalABCCompiler.TreeConverter.syntax_tree_visitor.convert_strong(statement st)
   в PascalABCCompiler.TreeConverter.syntax_tree_visitor.visit_program_code(statement_list program_code)
   в PascalABCCompiler.TreeConverter.syntax_tree_visitor.visit(block _block)
   в PascalABCCompiler.TreeConverter.syntax_tree_visitor.visit(program_module _program_module)
   в PascalABCCompiler.TreeConverter.SyntaxTreeToSemanticTreeConverter.CompileInterface(compilation_unit SyntaxUnit, unit_node_list UsedUnits, List`1 ErrorsList, List`1 WarningsList, SyntaxError parser_error, Hashtable bad_nodes, using_namespace_list namespaces, Dictionary`2 docs, Boolean debug, Boolean debugging)
   в PascalABCCompiler.Compiler.CompileUnit(unit_node_list Units, Dictionary`2 DirectCompilationUnits, unit_or_namespace SyntaxUsesUnit, String prev_path)
   в PascalABCCompiler.Compiler.Compile()'

Тема: гиперссылка нижнего колонтитула форума.

Вроде не битая по определению, но всё равно неприятно, но не знаю, куда обратиться:

Тема: переименование.

Не вникайте в нижеприведённый код, просто лень искать (Как называется самый маленький кусок кода, в котором воспроизводится ошибка? Вопрос не риторический.). Суть в том, что если нажать ПКМ на “i4” в строке for var i4 := 0 to a.Length - 1 do, потом “Переименовать”, ввести “i3” и подтвердить, оно сработает не на все переменные. А тут — на все:

## var a:=0;
Write(a);
Контекст
/// Получает имя exe без пути и расширения
function GetOnlyEXEFileName: string;
begin
  var a := LastPos('\', GetEXEFileName);
  Result := GetEXEFileName.Substring(a, GetEXEFileName.Length - a - 4);
end;

/// Переводит t в русское обозначение 
function DTtoR(t: System.DateTime) := t.Day.ToString + '.' + t.Month.ToString + '.' + t.Year.ToString + ' ' + t.TimeOfDay.ToString;

/// Записывает s в log
procedure WTF(s: string) := System.IO.File.AppendAllText(GetDir + '\' + GetOnlyEXEFileName + '.log', DTtoR(System.DateTime.Now) + ' ' + s + NewLine);

/// Изменяет размер окна и буфера консоли на w, h
procedure SCS(w, h: smallint);
begin
  Console.WindowWidth := integer(w).Clamp(15, Console.LargestWindowWidth);
  Console.BufferWidth := integer(w).Clamp(15, smallint.MaxValue - 1);
  Console.WindowHeight := integer(h).Clamp(1, Console.LargestWindowHeight);
  Console.BufferHeight := integer(h).Clamp(1, smallint.MaxValue - 1);
end;

/// Размер символа c в консоли. IPCIHS — являлся ли предыдущий выведенный символ верхней частью суррогатных пар.
function CCL(c: char; IPCIHS: boolean): smallint;
begin
  if IPCIHS then
    if char.IsHighSurrogate(c) then
      Result := 1
    else
      case c of
        char(7), char(8226): Result := 1;
        char(8), char(9688): Result := 0;
        char(9), char(9675):
          begin
            var NT: smallint := ((Console.CursorLeft + 1) div 8 + 1) * 8;
            if (NT < Console.BufferWidth) then
              Result := NT - Console.CursorLeft
            else
              Result := Console.BufferWidth - Console.CursorLeft + Min(8, Console.BufferWidth);
          end;
        char(10), char(9689): Result := Console.BufferWidth - Console.CursorLeft + (Console.CursorLeft = Console.BufferWidth - 1 ? Console.BufferWidth : 0);
        char(13), char(9834): Result := Console.CursorLeft = Console.BufferWidth - 1 ? 1 : -Console.CursorLeft;
      else Result := 2;
      end
  else
  if char.IsHighSurrogate(c) then
    Result := 0
  else
    case c of
      char(7), char(8226): Result := 0;
      char(8), char(9688): Result := (Console.CursorLeft = 0) ? 0 : -1;
      char(9), char(9675):
        begin
          var NT: smallint := (Console.CursorLeft div 8 + 1) * 8;
          if (NT < Console.BufferWidth) then
            Result := NT - Console.CursorLeft
          else
            Result := Console.BufferWidth - Console.CursorLeft;
        end;
      char(10), char(9689): Result := Console.BufferWidth - Console.CursorLeft;
      char(13), char(9834): Result := -Console.CursorLeft;
    else Result := 1;
    end;
end;

begin
  var a := Arr(0);
  var StopOnErr := true;
  SCS(Console.LargestWindowWidth, 3);
  var err := false;
  var x, y: smallint;
  var s: string;
  for var i2 := 0 to a.Length - 1 do
    for var i4 := 0 to a.Length - 1 do
    begin
      x := Console.CursorLeft;
      y := Console.CursorTop;
      s := char.ConvertFromUtf32(i2) + char.ConvertFromUtf32(i4);
      x += CCL(s[1], false);
      Write(s[1]);
      for var i := 2 to s.Length do
      begin
        x += CCL(s[i], char.IsHighSurrogate(s[i - 1]));
        Write(s[i]);
      end;
      y += x div Console.BufferWidth;
      x := x mod Console.BufferWidth;
      if (Console.CursorLeft <> x) or (Console.CursorTop <> y) then
      begin
        err := true;
        WTF(Format('Err: xe = {0}, xb = {1}, ye = {2}, yb = {3}', x, Console.CursorLeft, y, Console.CursorTop));
      end;
      if char.IsHighSurrogate(s[s.Length]) then
        Write(' ');
      Console.Clear;
      if err and StopOnErr then
        break;
    end;
  if err then
    Write('Ошибка!')
  else
    Write('Всё!');
  while true do
  begin
    Console.Beep;
  end;
end.

Тема: “юникодная консоль”.

Что изменится? Больше символов, вместо вопросительных знаков, или составные символы и суррогатные пары?

Где вы видели текстовые файлы в UTF-16? UTF-16 используется в вызовах Windows API и в файловой системе, а в файлах почти никогда не используется. В новых версиях Windows в консоли UTF-8 требуется по умолчанию.

К тому же в большинстве текстовых файлов не используется ничего кроме ASCII, а значит по умолчанию должна быть кодировка, которая при записи ASCII-символов их и пишет как есть. Из таких кодировок единственная, совместимая ещё и с юникодом — это UTF-8, так что выбора собственно и нет.

А она будет юникодной на основе UTF-16 или UTF-8? В новых версиях Windows примерно три года назад перешли на UTF-8 в консоли, но UTF-16 поддерживается для совместимости, хотя там недоступны новые возможности.

Как насчёт вывода в файл? Я предлагаю всё-таки сделать UTF-8 по умолчанию. А если нужен двоичный файл или требуется какая-то однобайтная кодировка вроде CP1251, то существуют нетипизированные файлы, которые просто file и file of byte / file of char.

Да, на Win10 с 2019 года. Но она далеко не у всех установлена, поэтому пока на это ориентироваться нельзя. “Внутри” PascalАВС.NET уже давно перешел на двухбайтную кодировку, но при работе с файлами в целях поддержки провозглашенной совместимости со старыми паскалями и Delphi нужна кодировка, позволяющая читать их старые файлы.

Внутренняя ошибка значит что произошла необработанная ошибка внутри компилятора, как тут. Это тоже всегда в issue.

Что касается решения - имя из наследника должно перекрывать унаследованное:

type
  t1 = class
    function f1(x: byte): word := 0;
  end;
  
  t2 = class(t1)
    const f1 = 'abc';
  end;
  
begin
  var a := t2.f1;
  a.GetType.ToString.Print;
end.

Ну а отчёт на самом деле мало что даёт. Он скорее для случаев когда ошибка воспроизводится только на вашей машине, но это редкость и я могу подтвердить что у меня так же.

Так и называется, минимальный код для воспроизведения.

И он таки нужен. В том 2-х строчном коде переименование работает. А в большом коде не понятно что именно стало причиной. Подозреваю что for в комбинации с чем то ещё, но нужен минимальный код.

А это и 2 темы?

Я — про конфликты внутри IDE, а не свой код, но и это интересно.

Да, b не переименовывается. Кстати, форматирование добавляет пробел в конце.

## for var a := 0 to 0 do
  for var b := 0 to 0 do;

В принципе можно залить в issue IDE, но я сам в этом копаться не буду, потому что у разработчиков позиция вроде “ошибки которые не должны происходить - должны быть неудобными, чтоб их хотелось залить в issue” - непобедимая логика.

В смысле? Я привёл пример того же случая (функция из базового класса и константа с тем же именем в наследнике), но с пользовательским кодом. Потому что типы из библиотек, в том числе стандартных, должны работать так же как пользовательские типы.

Вот мне неудобно вручную собирать zip с отчётом и исходниками.

Про for создать issue? Где?

Где наследники в случае issue?

1 лайк

UnicodeCategory, как и все остальные перечисления, наследует от System.Enum.

Переименование это фича IDE, поэтому и в issue его. Хорошее общее правило - проблемы компилятора это то что можно получить не используя IDE, то есть с консольным компилятором. Включая компиляцией из проводника при тыке на файл:
image

Не знал.

Тогда я ошибся и заложил в название другой смысл. Название правильное?

Но вы ведь нашли что конфликт идёт с Enum.Format. Откуда ему иначе взяться?

Та нет, вроде правильно. Хотя я бы назвал

Компилятор падает из за конфликта UnicodeCategory.Format и Enum.Format

Но это мой личный стиль, вы делайте как сами считайте правильным.

Именно поэтому и нужен UTF-8. В пределах ASCII он совместим со старыми кодировками, а что касается кириллицы, то в любом случае нужно заранее знать её кодировку (cp866, cp1251, utf-8), так как в TP и Delphi она разная. Переходить на запись в файл UTF-16 по умолчанию не следует. Так как в большинстве файлов, которые нужно читать из кода на паскале кириллицы не будет, а будет только ASCII (например списки чисел), то открывать файлы в UTF-8 по умолчанию вполне допустимо и желательно.

Вот с выводом в консоль — тут сложнее.

Исправили. Версия на сайте

Исправили. Версия на сайте

Ага, ага. Не будет. На ЕГЭ файлы, выгруженные из Word и Excel - они как раз с кириллицей.

То речь шла о совместимости с Turbo Pascal, то теперь Word и Excel… Ну там можно указать кодировку UTF-8 при экспорте, так что проблемы нет. В общем как я и говорил, для текстовых файлов какая-то другая кодировка по умолчанию не имеет смысла.