А предыдущий код не работает?
Да я, в общем-то, вам на слово верю. Просто у меня были проблемы в более сложных программах. Найду конкретный пример — опубликую.
type CNode<T> = class
where T: System.IComparable;
data: T;
left, right: CNode<T>;
end;
(7)procedure Insert<T>(var root: CNode<T>; x: T);
where T: System.IComparable;
begin
//..
end;
Program1.pas(7) : Невозможно инстанцировать, так как тип T не реализует интерфейс IComparable
type TA = class
public
function ToString(): string; override;
begin
result := 'meow';
end;
end;
type TB = class(TA)
end;
begin
var x := new TB();
writeln(x);
end.
output: ()
и в чем ошибка?
В первом случае в том, что тип T в строке 7 как раз реализует интерфейс IComparable, а во втором в том, что вывод должен быть meow. TB должен был унаследовать ToString от TA.
Нет, тут нет ошибки. Такова логика writeln - он выводит только публичные поля если в классе (не в предках) не определена ToString.
Да, тут ошибка компилятора. Будем разбираться
Данный код вызывает внутреннюю ошибку компилятора:
unit MyUnit1;
interface
function MyFunc1<T>(x: T): T;
where T: System.IComparable<T>;
function MyFunc2<T>(x: T): T;
where T: System.IComparable<T>;
implementation
function MyFunc1<T>(x: T): T;
begin
Result := x;
end;
function MyFunc2<T>(x: T): T;
begin
Result := MyFunc1(x);
end;
begin
end.
Ошибка происходит именно в том случае, когда определены две обобщенные функции с секцией where
(где вместо System.IComparable<T>
может быть указан любой интерфейс с шаблонным параметром), и одна функция в своем теле вызывает другую (либо вызывает сама себя рекурсивно).
Причем ошибки не происходит, если:
- Строки
where T: System.IComparable<T>;
заменить на
where T: System.IComparable;
- Не определять функции в интерфейсе модуля, а только в реализации, т. е. такой код не приводит к сбою:
unit MyUnit2;
interface
implementation
function MyFunc1<T>(x: T): T;
where T: System.IComparable<T>;
begin
Result := x;
end;
function MyFunc2<T>(x: T): T;
where T: System.IComparable<T>;
begin
Result := MyFunc1(x);
end;
begin
end.
Сообщение об ошибке:
23.05.2015 18:00:37
PascalABCCompiler.Core v2.2.0.951 (20.05.2015), debug version
Runtime version: 4.0.30319.34014
OS version: Microsoft Windows NT 6.2.9200.0
Processor count: 2
WorkingSet: 87288 kb
StatesList:
BeginCompileFile MyUnit1.pas
BeginParsingFile MyUnit1.pas
EndParsingFile MyUnit1.pas
ReadPCUFile PABCSystem.pcu
ReadDLL System.dll
ReadDLL mscorlib.dll
ReadDLL System.Core.dll
ReadDLL System.Numerics.dll
CompileInterface MyUnit1.pas
CompileImplementation MyUnit1.pas
CompilationFinished MyUnit1.pas
Ready
Error[0]: Внутренняя ошибка компилятора в модуле [pabcnetc.exe] :'System.Exception: System.ArgumentOutOfRangeException: Индекс за пределами диапазона. Индекс должен быть положительным числом, а его размер не должен превышать размер коллекции.
Имя параметра: index
в System.ThrowHelper.ThrowArgumentOutOfRangeException()
в PascalABCCompiler.TreeRealization.generic_convertions.determine_type(type_node tn, List`1 param_types, Boolean method_param_types)
в PascalABCCompiler.TreeRealization.generic_convertions.determine_type(type_node tn, List`1 param_types, Boolean method_param_types)
в PascalABCCompiler.TreeRealization.generic_parameter_eliminations.check_type_list(List`1 tparams, List`1 gpe_list, Boolean method_param_types, Int32& i)
в PascalABCCompiler.TreeRealization.common_namespace_function_node.get_instance(List`1 param_types, Boolean stop_on_error, location loc)
в PascalABCCompiler.TreeRealization.generic_convertions.DeduceFunction(function_node func, expressions_list fact, Boolean alone, location loc, List`1 syntax_nodes_parameters)
в PascalABCCompiler.TreeConverter.convertion_data_and_alghoritms.select_function(expressions_list parameters, SymbolInfo functions, location loc, List`1 syntax_nodes_parameters)
в PascalABCCompiler.TreeConverter.convertion_data_and_alghoritms.create_full_function_call(expressions_list exprs, SymbolInfo si, location loc, common_type_node converted_type, common_function_node top_function, Boolean allow_procedure)
в PascalABCCompiler.TreeConverter.syntax_tree_visitor.visit_method_call(method_call _method_call)
в PascalABCCompiler.TreeConverter.syntax_tree_visitor.visit(method_call _method_call)
в PascalABCCompiler.TreeConverter.returner.visit(expression expr)
в PascalABCCompiler.TreeConverter.syntax_tree_visitor.convert_strong(expression expr)
в PascalABCCompiler.TreeConverter.syntax_tree_visitor.visit(assign _assign)
в 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(procedure_definition _procedure_definition)
в PascalABCCompiler.TreeConverter.syntax_tree_visitor.visit(declarations _subprogram_definitions)
в PascalABCCompiler.TreeConverter.syntax_tree_visitor.visit(implementation_node _implementation_node)
в PascalABCCompiler.TreeConverter.syntax_tree_visitor.visit_implementation(unit_module _unit_module)
в PascalABCCompiler.TreeConverter.SyntaxTreeToSemanticTreeConverter.CompileImplementation(common_unit_node SemanticUnit, compilation_unit SyntaxUnit, unit_node_list UsedUnits, List`1 ErrorsList, List`1 WarningsList, SyntaxError parser_error, Hashtable bad_nodes, using_namespace_list interface_namespaces, using_namespace_list imlementation_namespaces, Dictionary`2 docs, Boolean debug, Boolean debugging)
в PascalABCCompiler.Compiler.CompileUnit(unit_node_list Units, unit_or_namespace SyntaxUsesUnit)
в PascalABCCompiler.Compiler.Compile()'
procedure GenericProceudre<T>(x: T);
begin
Seq(x).Print(); // Ошибка: нельзя преобразовать тип T к array of T
end;
но, в не generic процедуре всё хорошо
procedure NonGenericProceudre(x: integer);
begin
Seq(x).Print(); // Ошибки нет
end;
Создаю структуру данных Dictionary.
type
point = class
x, y: integer;
end;
Dic = Dictionary<string, point>;
begin
var d:= new Dic;
Этот Dictionary работает и может быть заполнен значениями, например:
d['key1'] := new point;
d['key1'].x:= 10;
d['key1'].y:= 20;
Но метод удаления по ключу (Remove) не работает:
d.Remove('key1');
Ошибка компиляции:
Нет перегруженной подпрограммы с таким количеством параметров
Remove можно применить, только если значения Dictionary являются стандартными типами данных, или же array of стандартный тип. Это ошибка, или так и должно быть? Но ведь сама структура Dictionary может быть создана с любым типом значений, почему же именно Remove не работает? Тем более, что “ключевой” тип здесь самый обычный String.
Только что скачал PABC, скопировал код и без проблем его запустил. Никаких ошибок нет. Ключ удаляется.
Обновил версию 966 на последнюю сборку 969 от 30.06.2015. И правда, заработало! Не успеваю уследить за темпами обновлений
Нет, всё таки есть еще некоторые негоразды с этим Dictionary. Вот объявлен и создан словарь:
type
point = class
x,y : integer;
end;
dic = Dictionary<string, point>;
begin
var d:= new dic;
d['key1']:= new point;
d['key1'].x:= 10;
d['key1'].y:= 20;
При попытке его обхода циклом foreach
Редактор почему то предполагает, что переменная цикла kv должна иметь поле x (как point). Но это нелепо, что и замечает компилятор:
foreach var kv in d do
writeln( kv.x );
x не объявлен в типе KeyValuePair<,>
Резонно, так как kv в данном случае, это пара (key, value) - KeyValuePair, поэтому должен иметь поля key и value. Но при попытке к ним обратиться:
foreach var kv in d do
writeln( kv.key );
или:
foreach var kv in d do
writeln( kv.value );
компиляция OK, но во время выполнения:
Ошибка времени выполнения: System.BadImageFormatException: Была сделана попытка загрузить программу, имеющую неверный формат. (Исключение из HRESULT: 0x8007000B)
Обращения сразу к полю x у значения не пропускается даже компилятором:
foreach var kv in d do
writeln( kv.value.x );
x не объявлен в типе
Обойти этот Dictionary можно только по ключам:
foreach var k in d.Keys do
writeln( d[k].x );
Вот теперь всё OK и при компиляции и при выполнении.
Что это, недоделки компилятора, или я чего то неправильно понимаю?
Версия программы:
Ошибка не воспроизводится. Давайте по порядку.
Ошибка времени выполнения: System.BadImageFormatException - это самая грозная ошибка. Думаю, она единственная, и система Intellisense по этой причине ничего не может распознать.
Опубликуйте здесь пожалуйста полный код программы, которая при выполнении падает с таким сообщением. В архив приложите pas и exe.
Еще попробуйте в конце программы написать writeln(d); Что получилось?
В архиве программа pas и откомпилированный exe Dictionary_error.zip (8.3 КБ)
Вот программа:
type
point = class
x, y: integer;
end;
Dic = Dictionary<string, point>;
begin
var d := new Dic;
d['key1']:= new point; // Один ключ инициализируется для примера
d['key1'].x:= 10; // если даже исключить эти строки
d['key1'].y:= 10; // при foreach ошибка будет такая же.
foreach var kv in d do
writeln(kv.key);
writeln(d); // программа вылетает не доходя до этого оператора
end.
Результат выполнения:
Ошибка времени выполнения: System.BadImageFormatException: An attempt was made to load a program with an incorrect format. (Exception from HRESULT: 0x8007000B)
Стек:
at dic_error2.Program.$Main()
at dic_error2.Program.Main()
Если убрать цикл foreach тогда writeln(d) нормально выводит:
{(key1,dic_error2.point)}
Да, увидели ошибку. Вывод типов неправильно работает. Будем исправлять.
Нет я рано радовался, что заработало. То есть оно временами заработало, но как то очень странно, и причина не в обновлении. Описываю как получить глюк, и как его “устранить”.
Делаем консольную программу:
type
point = class
x, y: integer;
end;
Dic = Dictionary<String, point>;
begin
var d := new Dic;
d.remove('key1');
end.
Компилируется нормально.
Теперь делаем новый проект и в файле описания формы в разделе implementation вставляем точно такой же код. Компилируем — всё OK. Сохраняем проект, закрываем среду pascal и открываем проект не через меню, а через файл на диске: Project.pabcproj
Компилируем:
Через меню открываем наше консольное приложение, которое ранее компилировалось, и получаем ту же ошибку в строке d.remove(‘key1’);
Нет перегруженной подпрограммы с таким количеством параметров
Закрываем pascal, открываем консольное приложение через pas файл — и оно опять компилируется! Интересно это только у меня такой глюк, или этот Dictionary больше никому не нужен? Воспроизведено на 3-х компьютерах: windows XP, 7, 10.
Удалите из проекта ссылку на System.Xml.Linq (в нем есть какой-то extension-метод remove, который всю малину портит) и перезапустите среду. Ошибку исправим
Честно говоря, так и думал, что с методами расширений очень скоро начнутся такие проблемы.