From f2df27dd87e9ed81048bc8eb580eae95c18f37fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=91=D0=BE=D0=BD=D0=B4=D0=B0=D1=80=D0=B5=D0=B2=20=D0=98?= =?UTF-8?q?=D0=B2=D0=B0=D0=BD?= Date: Thu, 3 Oct 2019 14:40:05 +0200 Subject: [PATCH] bug fix #2119 --- TestSuite/CompilationSamples/BlockFileOfT.pas | 417 +++++++++--------- TestSuite/generics30.pas | 23 + .../TreeConversion/syntax_tree_visitor.cs | 6 + 3 files changed, 232 insertions(+), 214 deletions(-) create mode 100644 TestSuite/generics30.pas diff --git a/TestSuite/CompilationSamples/BlockFileOfT.pas b/TestSuite/CompilationSamples/BlockFileOfT.pas index aea478c77..57acb6bdc 100644 --- a/TestSuite/CompilationSamples/BlockFileOfT.pas +++ b/TestSuite/CompilationSamples/BlockFileOfT.pas @@ -1,13 +1,14 @@ -//*****************************************************************************************************\\ + +//*****************************************************************************************************\\ // Copyright (©) Cergey Latchenko ( github.com/SunSerega | forum.mmcs.sfedu.ru/u/sun_serega ) // This code is distributed under the Unlicense -// For details see LICENSE.md file or this: -// https://github.com/SunSerega/PascalABC.Net-BlockFileOfT/blob/master/LICENSE.md +// For details see LICENSE file or this: +// https://github.com/SunSerega/POCGL/blob/master/LICENSE //*****************************************************************************************************\\ // Copyright (©) Сергей Латченко ( github.com/SunSerega | forum.mmcs.sfedu.ru/u/sun_serega ) -// Этот код распространяется под Unlicense -// Для деталей смотрите в файл LICENSE.md или это: -// https://github.com/SunSerega/PascalABC.Net-BlockFileOfT/blob/master/LICENSE.md +// Этот код распространяется с лицензией Unlicense +// Подробнее в файле LICENSE или тут: +// https://github.com/SunSerega/POCGL/blob/master/LICENSE //*****************************************************************************************************\\ ///Модуль, содержащий тип BlockFileOf @@ -27,8 +28,6 @@ type protected fi: FileInfo; protected _offset: int64; protected str: FileStream; - protected bw: BinaryWriter; - protected br: BinaryReader; protected linked := new List; @@ -41,69 +40,46 @@ type end; ///Тип, записывающий данные в файл по схожему с "file of " принципу - ///В отличие от типизированных файлов данный тип сохраняет всю запись одним блоком так как она записана в памяти. + ///Но, в отличии от типизированных файлов, данный тип сохраняет всю запись одним блоком так, как она записа в памяти. ///Это даёт значительное преимущество по скорости, но ограничивает типы, которые могут быть использованы в качестве полей типа /// - ///В частности, поля записи T и всех вложенных записей не могут быть: - /// - Указателями - /// - Ссылочными типами (классами, динамическими массивами) - /// - Типами char и System.DateTime - ///Эти ограничения можно обойти, об чем сказано в справке - BlockFileOf = class(BlockFileBase) + ///Это значит, что поля записи T и всех вложенных записей не могут быть: + /// - Указателями + /// - Ссылочными типами (классами / динамическими массивами) + ///Однако, эти ограничения можно обойти, об этом в справке + BlockFileOf = class(BlockFileBase) where T: record; + + {$region Internal} private static sz: integer; private static procedure TestForRefT(tt: System.Type); begin - if tt.IsClass then - begin - MessageBox(new System.IntPtr(nil), - $'Тип {tt} ссылочный.{#10}Ссылочные типы нельзя сохранять в типизированный файл.{#10}Нажмите OK для выхода.', - $'Тип T из BlockFileOf содержет ссылочные типы', - $10 - ); - Halt(-1); - end; - foreach var fi in - tt.GetFields( + if tt = typeof(System.IntPtr) then exit; // IntPtr содержит 1 поле типа pointer. Но IntPtr это не указатель а число, с размером как у pointer + + if tt.IsClass then raise new System.InvalidOperationException($'Тип {tt} ссылочный.{#10}Ссылочные типы нельзя сохранять в типизированный файл'); + + foreach var fi in tt.GetFields( System.Reflection.BindingFlags.GetField or System.Reflection.BindingFlags.Instance or System.Reflection.BindingFlags.Public or System.Reflection.BindingFlags.NonPublic ) do if not fi.IsLiteral then - if fi.FieldType <> tt then//integer имеет поле типа integer, без этой строчки StackOwerflow + if fi.FieldType <> tt then // тип integer имеет поле типа integer, без этой строчки StackOverflowException TestForRefT(fi.FieldType); end; private static constructor := try TestForRefT(typeof(T)); - - (* - try - var a := new T[0]; - GCHandle.Alloc(a,GCHandleType.Pinned).Free; - except - on e: System.ArgumentException do - begin - MessageBox(new System.IntPtr(nil), - $'.Net не принимает какой то из типов полей записи, для которой создан BlockFileOf{#10}Нажмите OK для выхода.', - $'Тип T содержит ссылочные типы', - $10 - ); - Halt(-1); - end; - end; - (**) - - sz := Marshal.SizeOf(typeof(T)); + sz := Marshal.SizeOf&; except on e:Exception do begin MessageBox(new System.IntPtr(nil), e.ToString, - $'При инициализации BlockFileOf<{typeof(T)}> произошла ошибка', + $'BlockFileOf<{typeof(T)}> не может инициализироваться:', $10 ); Halt(-1); @@ -126,7 +102,9 @@ type private function GetPosByte: int64; private procedure SetPosByte(pos: int64); - private function InternalReadLazy(c: integer; start_pos: int64):sequence of T; + {$endregion Internal} + + {$region constructor's} ///Инициализирует переменную файла, не привязывая её к файлу на диске public constructor := exit; @@ -136,11 +114,11 @@ type Assign(fname); ///Инициализирует переменную файла, привязывая её к файлу с именем fname - ///Устанавливает значение смещения от начала в байтах на offset + ///Устанавливает значение смещения от начала файла в байтах на offset public constructor(fname: string; offset: int64) := Assign(fname, offset); - ///- constructor BlockFileOf<>(f:BlockFileOf<>); + ///- constructor BlockFileOf<>(f: BlockFileOf<>); ///Инициализирует новую переменную, создавая связку с заданной переменной ///После вызова этого конструктора переданная и созданная переменные будут использовать общий файловый поток, но записывать разные типы данных (у них может быть разный T) ///Это значит, что переменная, которую передали в конструктор, уже должна иметь открытый файловый поток @@ -148,6 +126,10 @@ type public constructor(f: BlockFileBase) := Link(f); + {$endregion constructor's} + + {$region property's} + ///Возвращает или задаёт размер блока из одного элемента в байтах ///Задавать это свойство не рекомендуется public property TSize: integer read integer(sz) write sz := value; @@ -172,43 +154,39 @@ type ///Определяет, существует ли файл public property Exists: boolean read GetExists; - ///Возвращает номер текущего элемета в файле (нумеруя с 0) + ///Возвращает или задаёт номер текущего элемета в файле (нумеруя с 0) public property Pos: int64 read GetPos write SetPos; - ///Возвращает номер текущего байта от начала файла (нумеруя с 0) + ///Возвращает или задаёт номер текущего байта от начала файла (нумеруя с 0) public property PosByte: int64 read GetPosByte write SetPosByte; ///Определяет, привязана ли переменная к файлу - public property Assigned: boolean read fi <> nil; + public property Assigned: boolean read fi<>nil; ///Определяет, открыт ли файл - public property Opened: boolean read str <> nil; + public property Opened: boolean read str<>nil; ///Определяет, достигнут ли конец файла public property EOF: boolean read ByteSize-PosByte < sz; ///Возвращает поток текущего файла (или nil если файл не открыт) - ///Внимание! Любое действие, связанное с изменением данного потока файла, приведёт к неожиданным последствиям. Используйте его только если знаете, что вы делаете + ///Внимание! Любое действие, связанное с изменением данного потока файла приведёт к неожиданным последствиям, используйте его только если знаете, что вы делаете public property BaseStream: FileStream read str; - ///Возвращает BinaryWriter текущего файла (или nil если файл не открыт) - ///Внимание! Любое действие, связанное с изменением основного потока файла приведёт к неожиданным последствиям. Используйте его только если знаете, что вы делаете - public property BinWriter: BinaryWriter read bw; - - ///Возвращает BinaryReader текущего файла (или nil если файл не открыт) - ///Внимание! Любое действие, связанное с изменением основного потока файла приведёт к неожиданным последствиям. Используйте его только если знаете, что вы делаете - public property BinReader: BinaryReader read br; - - ///Возвращает FileInfo текущего файла (или nil если переменная не привязана к файлу) + ///Возвращает FileInfo текущего файла (или nil, если переменная не привязана к файлу) public property FileInfo: System.IO.FileInfo read fi; + {$endregion property's} + + {$region Setup IO} + ///Привязывает текущий экземпляр BlockFileOf к файлу с именем fname ///Привязывать можно и к несуществующим файлам, при открытии на запись будет создан новый файл public procedure Assign(fname: string); ///Привязывает данную переменную к файлу с именем fname ///Устанавливает смещение от начала файла до начала элементов в байтах на offset - ///Привязывать можно и к несуществующим файлам, при открытии с использованием Rewrite будет создан новый файл + ///Привязывать можно и к несуществующим файлам, при открытии чем то вроде Rewrite будет создан новый файл public procedure Assign(fname: string; offset: int64); ///Удаляет связь переменной с файлом, если связь есть @@ -224,7 +202,7 @@ type ///При указании иного расположения файл будет перемещён public procedure Rename(NewName: string); - ///Создает файл + ///Создает (или обнуляет) файл public procedure Rewrite; ///Привязывает данную переменную к файлу с именем fname и создает (или обнуляет) его @@ -254,20 +232,18 @@ type ///Устанавливает смещение от начала файла до начала элементов в байтах на offset public procedure Append(fname: string; offset: int64); - ///Ставит позицию в файле на элемент pos (нумеруя с 0) - public procedure Seek(pos: int64) := self.Pos := pos; - - ///Ставит файловый курсор на байт pos (нумеруя с 0) от начала файла - public procedure SeekByte(pos: int64) := self.PosByte := pos; - ///Записывает все изменения в файл и отчищает внутренние буферы ///До вызова Flush или Close все изменения и кеш хранятся в оперативной памяти - ///Поэтому, если вы проводите операции с большими объёмами памяти, лучше вызывать Flush время от времени + ///Если вы проводите операции с большими объёмами памяти - рекомендуется вызывать Flush время от времени public procedure Flush; ///Сохраняет и закрывает файл, если он открыт public procedure Close; + {$endregion Setup IO} + + {$region Write} + ///Записывает один элемент одним блоком в файл ///Переставляет файловый курсор на 1 элемет вперёд public procedure Write(o: T); @@ -296,6 +272,10 @@ type ///После каждого элемента переставляет файловый курсор на 1 элемет вперёд public procedure Write(o: sequence of T; from, count: integer); + {$endregion Write} + + {$region Read} + ///Читает один элемент из файла одним блоком ///Переставляет файловый курсор на 1 элемет вперёд public function Read: T; @@ -308,16 +288,45 @@ type ///И переставляет файловый курсор на count элеметов вперёд public function Read(start_elm, count: integer): array of T; + ///Читает один элемент из файла одним блоком и записывает в уже существующую переменную + ///Переставляет файловый курсор на 1 элемет вперёд + public procedure Read(var o: T); + + ///Читает o.Length элементов из файла одним блоком и записывает их в уже существующий массив + ///Переставляет файловый курсор на o.Length элеметов вперёд + public procedure Read(o: array of T); + + ///Читает count элементов одним блоком, начиная с элемента start_elm + ///Записывает их в массив o начиная с индекса arr_offset + ///И переставляет файловый курсор на count элеметов вперёд + public procedure Read(o: array of T; start_elm, arr_offset, count: integer); + + private function InternalReadLazy(c: integer; start_pos: int64): sequence of T; + ///Возвращает ленивую последовательность из count элементов ///После завершения чтения курсор окажется на последнем элементе возвращаемой последовательности - ///Каждый раз чтение будет происходить, начиная с элемента, на котором в данный момент стоит файловый курсор + ///Перед чтением каждого элемента файловый курсор переставляется на start_elm+n + ///Где start_elm - значение Pos на момент вызова ReadLazy, а n - количество уже считанных элементов public function ReadLazy(count: integer): sequence of T := InternalReadLazy(count, PosByte); ///Возвращает ленивую последовательность из count элементов, начиная с элемента start_elm ///После завершения чтения курсор окажется на последнем элементе возвращаемой последовательности - ///Каждый раз чтение будет происходить, начиная с элемента start_elm + ///Перед чтением каждого элемента файловый курсор переставляется на start_elm+n + ///Где n - количество уже считанных элементов public function ReadLazy(start_elm, count: integer): sequence of T := InternalReadLazy(count, _offset + start_elm*sz); + {$endregion Read} + + {$region Utils} + + ///Ставит позицию в файле на элемент pos (нумеруя с 0) + ///Рекомендуется использовать свойство Pos вместо данного метода + public procedure Seek(pos: int64) := self.Pos := pos; + + ///Ставит файловый курсор на байт pos (нумеруя с 0) от начала файла + ///Рекомендуется использовать свойство PosByte вместо данного метода + public procedure SeekByte(pos: int64) := self.PosByte := pos; + ///Возвращает ленивую последовательность из блоков-массивов с элементами типа T ///Каждый блок хранит такое количество байт, которое не превышает 4 КБ ///После прочтения каждого блока файловый корсор будет переставлен на его конец @@ -337,6 +346,8 @@ type Close; end; + {$endregion Utils} + end; {$region Exception's} @@ -352,7 +363,7 @@ type end; FileNotClosedException = class(Exception) constructor(fname:string) := - inherited Create($'Файл {fname} открыт, закройте его методом Close перед тем как продолжать'); + inherited Create($'Файл {fname} открыт, закройте его методом Close перед тем как продолжить'); end; CannotReadAfterEOF = class(Exception) constructor := @@ -377,8 +388,6 @@ begin end; self.str := f.str; - self.br := f.br; - self.bw := f.bw; self.fi := new FileInfo(f.fi.FullName); end; @@ -407,7 +416,7 @@ end; function BlockFileOf.GetFullName: string; begin if fi = nil then raise new FileNotAssignedException; - //fi.Refresh;//А тут не надо + //fi.Refresh; // А тут не надо Result := fi.FullName; end; @@ -417,12 +426,11 @@ function BlockFileOf.GetByteFileSize: int64; begin if fi = nil then raise new FileNotAssignedException; if str <> nil then + Result := str.Length else begin - Result := str.Length; - exit; + fi.Refresh; + Result := fi.Length; end; - fi.Refresh; - Result := fi.Length; end; procedure BlockFileOf.SetByteFileSize(size: int64); @@ -477,8 +485,6 @@ begin if fi = nil then raise new FileNotAssignedException; if str <> nil then raise new FileNotClosedException(fi.FullName); str := fi.Open(mode); - bw := new BinaryWriter(str); - br := new BinaryReader(str); end; procedure BlockFileOf.Delete; @@ -580,8 +586,6 @@ begin UnLink else str.Close; str := nil; - br := nil; - bw := nil; end; end; @@ -589,6 +593,16 @@ end; {$endregion Setup IO} +{$region IO Utils} + +procedure CopyMem(var o1: T1; var o2: T2; count: integer) := +System.Buffer.MemoryCopy( + @o1, @o2, + count, count +); + +{$endregion IO Utils} + {$region Write} procedure BlockFileOf.Write(o: T); @@ -596,17 +610,8 @@ begin if fi = nil then raise new FileNotAssignedException; if str = nil then raise new FileNotOpenedException(fi.FullName); var a := new byte[sz]; - var gc_hnd := GCHandle.Alloc(a, GCHandleType.Pinned); - try - System.Buffer.MemoryCopy( - @o, - gc_hnd.AddrOfPinnedObject.ToPointer, - sz,sz - ); - finally - gc_hnd.Free; - end; - bw.Write(a); + CopyMem(o,a[0],sz); + str.Write(a,0,sz); end; procedure BlockFileOf.Write(params o: array of T); @@ -616,58 +621,31 @@ begin var bl := sz*o.Length; var a := new byte[bl]; - var gc_hnd1 := GCHandle.Alloc(o, GCHandleType.Pinned); - try - var gc_hnd2 := GCHandle.Alloc(a, GCHandleType.Pinned); - try - System.Buffer.MemoryCopy( - gc_hnd1.AddrOfPinnedObject.ToPointer, - gc_hnd2.AddrOfPinnedObject.ToPointer, - bl,bl - ); - finally - gc_hnd2.Free; - end; - finally - gc_hnd1.Free; - end; - bw.Write(a); + CopyMem(o[0],a[0],bl); + str.Write(a,0,bl); end; procedure BlockFileOf.Write(o: ICollection); -type TArr = array of T; begin - if o is TArr(var a) then - begin - Write(a); - exit; - end; - if fi = nil then raise new FileNotAssignedException; if str = nil then raise new FileNotOpenedException(fi.FullName); - var a := new byte[sz*o.Count]; - var gc_hnd := GCHandle.Alloc(a, GCHandleType.Pinned); - try - var hnd := gc_hnd.AddrOfPinnedObject; - foreach var el in o do - begin - System.Buffer.MemoryCopy( - @el, - hnd.ToPointer, - sz,sz - ); - System.IntPtr.Add(hnd, sz); - end; - finally - gc_hnd.Free; + var bl := sz*o.Count; + var a := new byte[bl]; + + var p := 0; + var enm: IEnumerator := o.GetEnumerator(); + while enm.MoveNext do + begin + var v := enm.Current; + CopyMem(v,a[p],sz); + p += sz; end; - bw.Write(a); + + str.Write(a,0,bl); end; procedure BlockFileOf.Write(o: sequence of T) := -if o is ICollection(var c) then - Write(c) else foreach var el in o do Write(el); @@ -675,65 +653,37 @@ procedure BlockFileOf.Write(o: array of T; from, count: integer); begin if fi = nil then raise new FileNotAssignedException; if str = nil then raise new FileNotOpenedException(fi.FullName); + if from+count > o.Length then raise new System.IndexOutOfRangeException; var bl := sz*count; var a := new byte[bl]; - var gc_hnd1 := GCHandle.Alloc(o, GCHandleType.Pinned); - try - var gc_hnd2 := GCHandle.Alloc(a, GCHandleType.Pinned); - try - System.Buffer.MemoryCopy( - System.IntPtr.Add(gc_hnd1.AddrOfPinnedObject, from * sz).ToPointer, - gc_hnd2.AddrOfPinnedObject.ToPointer, - bl,bl - ); - finally - gc_hnd2.Free; - end; - finally - gc_hnd1.Free; - end; - bw.Write(a); + CopyMem(o[from],a[0],bl); + str.Write(a,0,bl); end; procedure BlockFileOf.Write(o: ICollection; from, count: integer); -type TArr = array of T; begin - if o is TArr(var a) then - begin - Write(a, from, count); - exit; - end; - if fi = nil then raise new FileNotAssignedException; if str = nil then raise new FileNotOpenedException(fi.FullName); + if from+count > o.Count then raise new System.IndexOutOfRangeException; - var a := new byte[sz*o.Count]; - var gc_hnd := GCHandle.Alloc(a, GCHandleType.Pinned); - try - var hnd := gc_hnd.AddrOfPinnedObject; - foreach var el in o do - if from > 0 then from -= 1 else - if count > 0 then - begin - System.Buffer.MemoryCopy( - @el, - hnd.ToPointer, - sz,sz - ); - hnd := System.IntPtr.Add(hnd, sz); - count -= 1; - end; - finally - gc_hnd.Free; + var bl := sz*count; + var a := new byte[bl]; + + var p := 0; + var enm: IEnumerator := o.Skip(from).Take(count).GetEnumerator; + while enm.MoveNext do + begin + var v := enm.Current; + CopyMem(v,a[p],sz); + p += sz; end; - bw.Write(a); + + str.Write(a,0,bl); end; procedure BlockFileOf.Write(o: sequence of T; from, count: integer) := -if o is ICollection(var c) then - Write(c,from,count) else - Write(o.Skip(from).Take(count)); +Write(o.Skip(from).Take(count)); {$endregion Write} @@ -745,17 +695,10 @@ begin if str = nil then raise new FileNotOpenedException(fi.FullName); if str.Length - str.Position < sz then raise new CannotReadAfterEOF; - var a := br.ReadBytes(sz); - var gc_hnd := GCHandle.Alloc(a, GCHandleType.Pinned); - try - System.Buffer.MemoryCopy( - gc_hnd.AddrOfPinnedObject.ToPointer, - @Result, - sz,sz - ); - finally - gc_hnd.Free; - end; + var a := new byte[sz]; + str.Read(a,0,sz); + CopyMem(a[0],Result,sz); + end; function BlockFileOf.Read(count: integer): array of T; @@ -765,48 +708,90 @@ begin if str.Length - str.Position < sz*count then raise new CannotReadAfterEOF; var bl := sz*count; - var a := br.ReadBytes(bl); + var a := new byte[bl]; + str.Read(a,0,bl); Result := new T[count]; - var gc_hnd1 := GCHandle.Alloc(a, GCHandleType.Pinned); - try - var gc_hnd2 := GCHandle.Alloc(Result, GCHandleType.Pinned); - try - System.Buffer.MemoryCopy( - gc_hnd1.AddrOfPinnedObject.ToPointer, - gc_hnd2.AddrOfPinnedObject.ToPointer, - bl,bl - ); - finally - gc_hnd2.Free; - end; - finally - gc_hnd1.Free; - end; + CopyMem(a[0],Result[0],bl); + end; function BlockFileOf.Read(start_elm, count:integer): array of T; begin + if fi = nil then raise new FileNotAssignedException; + if str = nil then raise new FileNotOpenedException(fi.FullName); + if str.Length - str.Position < sz*count then raise new CannotReadAfterEOF; + Pos := start_elm; Result := Read(count); + end; +procedure BlockFileOf.Read(var o: T); +begin + if fi = nil then raise new FileNotAssignedException; + if str = nil then raise new FileNotOpenedException(fi.FullName); + if str.Length - str.Position < sz then raise new CannotReadAfterEOF; + + var a := new byte[sz]; + str.Read(a,0,sz); + CopyMem(a[0],o,sz); + +end; + +procedure BlockFileOf.Read(o: array of T); +begin + var bl := sz*o.Length; + + if fi = nil then raise new FileNotAssignedException; + if str = nil then raise new FileNotOpenedException(fi.FullName); + if str.Length - str.Position < bl then raise new CannotReadAfterEOF; + + var a := new byte[bl]; + str.Read(a,0,bl); + CopyMem(a[0],o[0],bl); + +end; + +procedure BlockFileOf.Read(o: array of T; start_elm, arr_offset, count: integer); +begin + var bl := sz*count; + + if fi = nil then raise new FileNotAssignedException; + if str = nil then raise new FileNotOpenedException(fi.FullName); + if str.Length - str.Position < bl then raise new CannotReadAfterEOF; + if arr_offset+count > o.Length then raise new System.IndexOutOfRangeException; + + Pos := start_elm; + var a := new byte[bl]; + str.Read(a,0,bl); + CopyMem(a[0],o[start_elm],bl); + +end; + +{$endregion Read} + +{$region Utils} + function BlockFileOf.InternalReadLazy(c: integer; start_pos: int64): sequence of T; begin if fi = nil then raise new FileNotAssignedException; if str = nil then raise new FileNotOpenedException(fi.FullName); if str.Length - start_pos < sz*c then raise new CannotReadAfterEOF; - for var i := 0 to c-1 do + loop c do begin - PosByte := start_pos + i*sz; + PosByte := start_pos; yield Read; + start_pos += sz; end; + end; function BlockFileOf.ToSeqBlocks(blocks_size: integer): sequence of array of T; begin var c := blocks_size div sz; var i := 0; + while true do begin var left := Size - i; @@ -825,20 +810,24 @@ begin yield Read(c); i += c; end; + end; + end; function BlockFileOf.ToSeq: sequence of T; begin var i := 0; + while not EOF do begin Pos := i; yield Read; i += 1; end; + end; -{$endregion Read} +{$endregion Utils} end. \ No newline at end of file diff --git a/TestSuite/generics30.pas b/TestSuite/generics30.pas new file mode 100644 index 000000000..5be2fefe1 --- /dev/null +++ b/TestSuite/generics30.pas @@ -0,0 +1,23 @@ +type + TErr = class; + + TBase = abstract class + public static procedure p1(temp: TErr); + + end; + + TErr = class(TBase) end; + +static procedure TBase.p1(temp: TErr); +begin + var q: TErr := new TErr; + var a1: TBase := q; + var a2: TBase := q as TBase; + assert(a2 <> nil); + q := a1 as TErr; + assert(q <> nil); +end; + +begin + TBase.p1&(nil); +end. \ No newline at end of file diff --git a/TreeConverter/TreeConversion/syntax_tree_visitor.cs b/TreeConverter/TreeConversion/syntax_tree_visitor.cs index c2d7046fa..200f01b19 100644 --- a/TreeConverter/TreeConversion/syntax_tree_visitor.cs +++ b/TreeConverter/TreeConversion/syntax_tree_visitor.cs @@ -3684,6 +3684,12 @@ namespace PascalABCCompiler.TreeConverter AddError(get_location(_class_definition.class_parents.types[0]), "CAN_NOT_INHERIT_FROM_GENERIC_PARAMETER"); } context.converted_type.SetBaseType(tn); + var type_instances = generic_convertions.get_type_instances(context.converted_type); + if (type_instances != null) + foreach (generic_type_instance_info gti in type_instances) + { + gti.pseudo_instance.SetBaseType(tn); + } } //Теперь добавляем интерфейсы. //Цикл с единицы, т.к. нулевой элемент уже был рассмотрен.