Merge pull request #3170 from samuraiGH/ForeachResolver-rework

Foreach resolver rework
This commit is contained in:
Ivan Bondarev 2024-08-12 15:24:11 +02:00 committed by GitHub
commit 0d846d2335
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 597 additions and 229 deletions

View file

@ -1,11 +1,12 @@
// Copyright (c) Ivan Bondarev, Stanislav Mikhalkovich (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
//ѕри внесении изменений в структуру PCU файла версию увеличить на 1
// при внесении изменений в структуру PCU файла версию увеличить на 1
namespace PascalABCCompiler.PCU
{
public static class PCUFileFormatVersion
{
public static System.Int16 Version = 120;
public static System.Int16 Version = 121;
}
}

View file

@ -3238,7 +3238,12 @@ namespace PascalABCCompiler.PCU
var_definition_node vdn = GetLocalOrNamespaceVariableByOffset(br.ReadInt32());
expression_node expr = CreateExpression();
statement_node body = CreateStatement();
return new foreach_node(vdn, expr, body, null);
type_node elementType = null;
if (CanReadObject())
elementType = GetTypeReference();
bool isGeneric = br.ReadBoolean();
return new foreach_node(vdn, expr, body, elementType, isGeneric, null);
}
private statement_node CreateLock()

View file

@ -3558,6 +3558,9 @@ namespace PascalABCCompiler.PCU
bw.Write(GetMemberOffset(fn.ident));
VisitExpression(fn.in_what);
VisitStatement(fn.what_do);
if (CanWriteObject(fn.element_type))
WriteTypeReference(fn.element_type);
bw.Write(fn.is_generic);
}
private void VisitLock(lock_statement node)

View file

@ -866,58 +866,6 @@ namespace PascalABCCompiler.NETGenerator {
return null;
}
public MethodInfo GetEnumeratorMethod(Type t, out Type[] generic_args)
{
generic_args = null;
Type generic_def = null;
if (t.IsGenericType && !t.IsGenericTypeDefinition)
generic_def = t.GetGenericTypeDefinition();
else
generic_def = t;
if (generic_def.IsArray && generic_def.GetElementType().IsGenericParameter)
{
return TypeFactory.IEnumerableGenericType.GetMethod("GetEnumerator");
}
if (generic_def.IsArray)
{
if (IsConstructedGenericType(generic_def.GetElementType()))
return TypeBuilder.GetMethod(TypeFactory.IEnumerableGenericType.MakeGenericType(generic_def.GetElementType()), TypeFactory.IEnumerableGenericType.GetMethod("GetEnumerator"));
else
return TypeFactory.IEnumerableGenericType.MakeGenericType(generic_def.GetElementType()).GetMethod("GetEnumerator");
}
else if (generic_def.IsGenericParameter)
{
return TypeFactory.IEnumerableType.GetMethod("GetEnumerator", Type.EmptyTypes);
}
foreach (Type interf in generic_def.GetInterfaces())
{
if (interf.IsGenericType && interf.GetGenericTypeDefinition() == TypeFactory.IEnumerableGenericType)
{
MethodInfo mi = interf.GetGenericTypeDefinition().GetMethod("GetEnumerator");
if (generic_def != t)
{
if (t.GetGenericArguments().Length != interf.GetGenericTypeDefinition().GetGenericArguments().Length)
return null;
Type gt = interf.GetGenericTypeDefinition().MakeGenericType(t.GetGenericArguments());
if (IsConstructedGenericType(gt))
return TypeBuilder.GetMethod(gt, mi);
else
return interf.GetGenericTypeDefinition().MakeGenericType(t.GetGenericArguments()).GetMethod("GetEnumerator");
}
else if (IsConstructedGenericType(interf))
{
//return TypeBuilder.GetMethod(TypeFactory.IEnumerableGenericType.MakeGenericType(interf.GetGenericArguments()), TypeFactory.IEnumerableGenericType.GetMethod("GetEnumerator"));
//return TypeFactory.IEnumerableType.GetMethod("GetEnumerator", Type.EmptyTypes);
generic_args = interf.GetGenericArguments();
return TypeBuilder.GetMethod(interf, mi);
}
else
return interf.GetMethod("GetEnumerator");
}
}
return TypeFactory.IEnumerableType.GetMethod("GetEnumerator", Type.EmptyTypes);
}
public void SetAsProcessing(ICommonTypeNode type)
{
processing_types[type] = true;

View file

@ -11601,40 +11601,46 @@ namespace PascalABCCompiler.NETGenerator
public override void visit(IForeachNode value)
{
VarInfo vi = helper.GetVariable(value.VarIdent);
//Type interf = helper.GetTypeReference(value.InWhatExpr.type).tp;
Type var_tp = helper.GetTypeReference(value.VarIdent.type).tp;
//(ssyy) 12.04.2008 Поиск IEnumerable не нужен! Это дело семантики!
Type in_what_type = helper.GetTypeReference(value.InWhatExpr.type).tp;
Type return_type = null;
bool is_generic = false;
Type[] generic_args = null;
MethodInfo enumer_mi = null; //typeof(System.Collections.IEnumerable).GetMethod("GetEnumerator", Type.EmptyTypes);
if (/*var_tp.IsValueType &&*/ !var_tp.IsGenericParameter && !(in_what_type.IsArray && in_what_type.GetArrayRank() > 1))
Type return_type;
MethodInfo enumer_mi;
Type elementType = helper.GetTypeReference(value.ElementType).tp;
bool is_generic = value.IsGeneric;
if (is_generic)
{
enumer_mi = helper.GetEnumeratorMethod(in_what_type, out generic_args);
if (enumer_mi == null)
// если элемент перечисления объявлен в коде
// или типоаргумент элемента перечисления объявлен в коде
// например IEnumerable<MyType>, array of MyType
if (helper.IsConstructedGenericType(elementType))
{
enumer_mi = typeof(System.Collections.IEnumerable).GetMethod("GetEnumerator", Type.EmptyTypes);
return_type = enumer_mi.ReturnType;
enumer_mi = TypeBuilder.GetMethod(
TypeFactory.IEnumerableGenericType.MakeGenericType(elementType),
TypeFactory.IEnumerableGenericType.GetMethod("GetEnumerator")
);
// IEnumerator<elementType>
return_type = enumer_mi.ReturnType
.GetGenericTypeDefinition()
.MakeGenericType(elementType);
}
// для полностью скомпилированных типов TypeBuilder не требуется
// IEnumerable<integer>, array of string
else
{
is_generic = enumer_mi.ReturnType.IsGenericType;
enumer_mi = TypeFactory.IEnumerableGenericType.MakeGenericType(elementType).GetMethod("GetEnumerator");
return_type = enumer_mi.ReturnType;
if (in_what_type.IsGenericType && return_type.IsGenericType && !return_type.IsGenericTypeDefinition)
return_type = return_type.GetGenericTypeDefinition().MakeGenericType(in_what_type.GetGenericArguments());
else if (in_what_type.IsArray && return_type.IsGenericType && !return_type.IsGenericTypeDefinition)
return_type = return_type.GetGenericTypeDefinition().MakeGenericType(in_what_type.GetElementType());
else if (generic_args != null)
return_type = return_type.GetGenericTypeDefinition().MakeGenericType(generic_args);
}
}
else
{
enumer_mi = typeof(System.Collections.IEnumerable).GetMethod("GetEnumerator", Type.EmptyTypes);
enumer_mi = TypeFactory.IEnumerableType.GetMethod("GetEnumerator");
return_type = enumer_mi.ReturnType;
}
LocalBuilder lb = il.DeclareLocal(return_type);
if (save_debug_info) lb.SetLocalSymInfo("$enumer$" + uid++);

View file

@ -2350,6 +2350,16 @@ namespace PascalABCCompiler.SemanticTree
{
get;
}
ITypeNode ElementType
{
get;
}
bool IsGeneric
{
get;
}
}
public interface ILockStatement : IStatementNode, ILocated

View file

@ -0,0 +1,20 @@
//!Невозможно выполнить оператор foreach или yield sequence по выражению типа c1
uses System.Collections, System.Collections.Generic;
type
c1 = class (IEnumerable<integer>, IEnumerable<string>)
public
function IEnumerable<string>.GetEnumerator: IEnumerator<string>;
begin end;
function IEnumerable<integer>.GetEnumerator: IEnumerator<integer>;
begin end;
function IEnumerable.GetEnumerator: IEnumerator;
begin end;
end;
begin
foreach var item in new c1 do ;
end.

View file

@ -0,0 +1,48 @@
{$reference 'foreach_resolver_lib.dll'}
type
c0 = class
procedure pr:= exit;
end;
begin
var v1:= new c1<integer, c0>;
v1._element:= new c0;
foreach var item in v1 do
item.pr;
Assert(check);
check:= false;
var v2:= new c2;
v2._element:= 'myStr';
foreach var item in v2 do
item.Replace('my', 'your');
Assert(check);
check:= false;
var v3:= new c3;
v3._element:= 'myStr';
foreach var item in v3 do
print(item);
Assert(check);
check:= false;
var v4:= new c4;
v4._element:= 'myStr';
foreach var item in v4 do
item.Replace('my', 'your');
Assert(check);
check:= false;
var v5:= new c5;
v5._element:= 4;
foreach var item in v5 do
Assert(item = 4);
Assert(check);
end.

View file

@ -0,0 +1,67 @@
uses System.Collections, System.Collections.Generic;
type
c1 = class (IEnumerable<integer>)
public
function IEnumerable<integer>.GetEnumerator: IEnumerator<integer>;
begin
Assert(false);
end;
function GetEnumerator: IEnumerator;
begin
result:= Seq&<object>().GetEnumerator;
end;
end;
c2<T1, T2> = class (IEnumerable<T2>)
public
function GetEnumerator: IEnumerator<T2>;
begin
result:= Seq&<T2>().GetEnumerator;
end;
function IEnumerable.GetEnumerator: IEnumerator;
begin
Assert(false);
end;
end;
c3 = class (IEnumerable<integer>, IEnumerable<string>)
public
function GetEnumerator: IEnumerator<string>;
begin
result:= Seq&<string>().GetEnumerator;
end;
function IEnumerable<integer>.GetEnumerator: IEnumerator<integer>;
begin
Assert(false);
end;
function IEnumerable.GetEnumerator: IEnumerator;
begin
Assert(false);
end;
end;
c4 = class (c2<integer, int64>)
end;
begin
var v1:= new c1;
foreach var item in v1 do ;
var v2:= new c2<byte, string>;
foreach var item in v2 do ;
var v3:= new c3;
foreach var item in v3 do ;
var v4:= new c4;
foreach var item in v4 do ;
end.

Binary file not shown.

View file

@ -0,0 +1,58 @@
library foreach_resolver_lib;
uses System.Collections, System.Collections.Generic;
var check:= false;
type
c1<T1, T2> = class (IEnumerable<T1>, IEnumerable<T2>)
public
_element: T2;
function GetEnumerator: IEnumerator<T2>;
begin
check:= true;
result:= lst( _element ).GetEnumerator;
end;
function IEnumerable<T1>.GetEnumerator: IEnumerator<T1>;
begin end;
function IEnumerable.GetEnumerator: IEnumerator;
begin end;
end;
c2 = class (IEnumerable<integer>, IEnumerable<string>)
public
_element: string;
function GetEnumerator: IEnumerator<string>;
begin
check:= true;
result:= lst(_element).GetEnumerator;
end;
function IEnumerable<integer>.GetEnumerator: IEnumerator<integer>;
begin end;
function IEnumerable.GetEnumerator: IEnumerator;
begin end;
end;
c3 = class (IEnumerable<string>)
public
_element: object;
function IEnumerable<string>.GetEnumerator: IEnumerator<string>;
begin end;
function GetEnumerator: IEnumerator;
begin
check:= true;
result:= |_element|.GetEnumerator;
end;
end;
c4 = class (c2) end;
c5 = class (c1<string, integer>) end;
end.

View file

@ -641,8 +641,8 @@ namespace TreeConverter.LambdaExpressions.Closure
type_node elemType = null;
if (inWhat.type == null)
inWhat = tmp;
bool bb; // здесь bb не нужно. Оно нужно в foreach
_visitor.FindIEnumerableElementType(/*_foreach_stmt, */inWhat.type, ref elemType, out bb);
bool sys_coll_ienum;
_visitor.FindIEnumerableElementType(/*_foreach_stmt, */inWhat.type, ref elemType, out sys_coll_ienum);
if (_foreach_stmt.type_name == null)
{
@ -676,7 +676,7 @@ namespace TreeConverter.LambdaExpressions.Closure
if (!(vdn.type is compiled_generic_instance_type_node))
_visitor.convertion_data_and_alghoritms.check_convert_type_with_inheritance(vdn.type, elemType, _visitor.get_location(_foreach_stmt.identifier));
var fn = new foreach_node(vdn, inWhat, null, _visitor.get_location(_foreach_stmt));
var fn = new foreach_node(vdn, inWhat, null, elemType, !sys_coll_ienum, _visitor.get_location(_foreach_stmt));
_visitor.context.enter_in_cycle(fn);
_visitor.context.loop_var_stack.Push(vdn);

View file

@ -36,7 +36,9 @@ namespace PascalABCCompiler.TreeConverter
expression_node foreachCollection;
var_definition_node foreachVariable;
ForeachCheckAndConvert(_foreach_stmt, out foreachCollection, out foreachVariable);
type_node elementType;
bool isGeneric;
ForeachCheckAndConvert(_foreach_stmt, out foreachCollection, out foreachVariable, out elementType, out isGeneric);
definition_node dnind = null;
var_definition_node vdn = null;
@ -54,7 +56,14 @@ namespace PascalABCCompiler.TreeConverter
statements_list sl = new statements_list(get_location(_foreach_stmt.stmt));
convertion_data_and_alghoritms.statement_list_stack_push(sl);
foreach_node foreachNode = new foreach_node(foreachVariable, foreachCollection, null, get_location(_foreach_stmt));
foreach_node foreachNode = new foreach_node(
foreachVariable,
foreachCollection,
null,
elementType,
isGeneric,
get_location(_foreach_stmt)
);
context.enter_in_cycle(foreachNode);
context.loop_var_stack.Push(foreachVariable);
@ -115,8 +124,13 @@ namespace PascalABCCompiler.TreeConverter
}
private void ForeachCheckAndConvert(foreach_stmt _foreach_stmt, out expression_node foreachCollection,
out var_definition_node foreachVariable)
private void ForeachCheckAndConvert(
foreach_stmt _foreach_stmt,
out expression_node foreachCollection,
out var_definition_node foreachVariable,
out type_node elementType,
out bool isGenereic
)
{
var lambdaSearcher = new LambdaSearcher(_foreach_stmt.in_what);
if (lambdaSearcher.CheckIfContainsLambdas())
@ -135,6 +149,9 @@ namespace PascalABCCompiler.TreeConverter
if (!FindIEnumerableElementType(foreachCollection.type, ref elem_type, out sys_coll_ienum))
AddError(foreachCollection.location, "CAN_NOT_EXECUTE_FOREACH_BY_EXPR_OF_TYPE_{0}", foreachCollection.type.name);
elementType = elem_type;
isGenereic = !sys_coll_ienum;
var vars = _foreach_stmt.ext as ident_list;
if (vars != null)
{

View file

@ -25,6 +25,8 @@ using while_node = PascalABCCompiler.TreeRealization.while_node;
using TreeConverter.LambdaExpressions.Closure;
using TreeConverter.LambdaExpressions;
using PascalABCCompiler.TreeConverter.TreeConversion;
using System.Reflection;
using PascalABCCompiler.SemanticTree;
namespace PascalABCCompiler.TreeConverter
{
@ -13723,7 +13725,7 @@ namespace PascalABCCompiler.TreeConverter
cmc.parameters.AddElement(new int_const_node((prm.type.element_type.element_type as short_string_type_node).Length,null));
}
what_do.statements.AddElement(cmc);
foreach_node fn = new foreach_node(var,in_what,what_do,null);
foreach_node fn = new foreach_node(var, in_what,what_do, prm.type.element_type, true, null);
sl.statements.AddElementFirst(fn);
}
}
@ -18488,179 +18490,328 @@ namespace PascalABCCompiler.TreeConverter
return false;
}
private bool IsIEnumerableInterface(Type t)
{
var IEnumType = typeof(IEnumerable);
var IEnumTypedType = typeof(IEnumerable<>);
var isIEnumType = (t == IEnumType);
var isIEnumTypedType = t.IsGenericType
&& (t.GetGenericTypeDefinition() == IEnumTypedType
);
return isIEnumType || isIEnumTypedType;
}
private bool IsIEnumeratorInterface(Type t)
{
var IEnumType = typeof(IEnumerator);
var IEnumTypedType = typeof(IEnumerator<>);
var isIEnumType = (t == IEnumType);
var isIEnumTypedType = t.IsGenericType
&& (t.GetGenericTypeDefinition() == IEnumTypedType
);
return isIEnumType || isIEnumTypedType;
}
private Type FindIEnumerableInterfaceInCompiledType(Type compiledType)
{
var IEnumType = typeof(IEnumerable);
var IEnumTypedType = typeof(IEnumerable<>);
// если тип является интерфейсом IEnumerable или IEnumerable<T> то берём его
if (IsIEnumerableInterface(compiledType))
return compiledType;
// иначе ищем подходящие интерфейсы в иерархии
var filteredInterfaces = compiledType.FindInterfaces(
(item, _) => IsIEnumerableInterface(item),
null
);
var genericInterfaceCount = filteredInterfaces.Count(item => item.IsGenericType);
if (compiledType.IsInterface)
{
// интерфейс наследует несколько IEnumerable<T> с разными T
// невозможно выбрать. требуется явное приведение
if (genericInterfaceCount > 1)
return null;
if (genericInterfaceCount == 1)
return filteredInterfaces.First(item => item.IsGenericType);
else
return IEnumType;
}
// для класса/записи необходимо учитывать явные реализации GetEnumerator
var methods = compiledType.GetMethods().Where(item =>
item.Name == "GetEnumerator"
&& item.GetParameters().Length == 0
&& IsIEnumeratorInterface(item.ReturnType)
).ToArray();
switch (methods.Length)
{
case 0:
// тип реализует явно несколько IEnumerable<T>
// невозможно выбрать. требуется явное приведение
if (genericInterfaceCount > 1)
return null;
// тип реализует и IEnumerable и IEnumerable<T> явно
// выбираем IEnumerable<T>
if (genericInterfaceCount == 1)
return filteredInterfaces.First(item => item.IsGenericType);
// явно реализует IEnumerable
else
return IEnumType;
case 1:
var method = methods[0];
if (!method.ReturnType.IsGenericType)
return IEnumType;
else
{
var elementType = method.ReturnType.GetGenericArguments().First();
return IEnumTypedType.MakeGenericType(elementType);
}
default:
// в типе наследнике перекрыли GetEnumerator
// невозможно точно выбрать метод
return null;
}
}
private bool IsIEnumerableInterface(type_node t)
{
compiled_type_node orig;
if (t is compiled_type_node)
orig = t as compiled_type_node;
else if (t is compiled_generic_instance_type_node)
orig = (t as compiled_generic_instance_type_node).original_generic as compiled_type_node;
else
return false;
return IsIEnumerableInterface(orig.compiled_type);
}
/// <summary>
/// Собирает интерфейсы, реализованные типом и его предками
/// </summary>
private List<type_node> CollectInterfacesFromTypeNode(type_node node)
{
var result = new List<ITypeNode>();
result.AddRange(node.ImplementingInterfaces);
if (node.base_type != null)
{
var interfInBase = CollectInterfacesFromTypeNode(node.base_type);
result.AddRange(interfInBase);
}
return result.Cast<type_node>().ToList();
}
private bool IsIEnumeratorInterface(type_node t)
{
compiled_type_node orig;
if (t is compiled_type_node)
orig = t as compiled_type_node;
else if (t is compiled_generic_instance_type_node)
orig = (t as compiled_generic_instance_type_node).original_generic as compiled_type_node;
else
return false;
return IsIEnumeratorInterface(orig.compiled_type);
}
private type_node FindIEnumerableInterfaceInTypeNode(type_node node)
{
var IEnumType = compiled_type_node.get_type_node( typeof(IEnumerable) );
var IEnumTypedType = compiled_type_node.get_type_node( typeof(IEnumerable<>) );
var filteredInterfaces = CollectInterfacesFromTypeNode(node)
.Where(item => IsIEnumerableInterface(item))
.ToArray();
var genericInterfaceCount = filteredInterfaces.Count(item => item.is_generic_type_instance);
if (node.IsInterface)
{
// интерфейс наследует несколько IEnumerable<T> с разными T
// невозможно выбрать. требуется явное приведение
if (genericInterfaceCount > 1)
return null;
if (genericInterfaceCount == 1)
return filteredInterfaces.First(item => item.is_generic_type_instance);
else
return IEnumType;
}
var commonType = (common_type_node)node;
var methods = commonType.methods.Where(item =>
item.name.Equals("GetEnumerator", StringComparison.OrdinalIgnoreCase)
&& item.parameters.Count == 0
&& IsIEnumeratorInterface(item.return_value_type)
).ToArray();
switch (methods.Length)
{
case 0:
// тип реализует явно несколько IEnumerable<T>
// невозможно выбрать. требуется явное приведение
if (genericInterfaceCount > 1)
return null;
// тип реализует и IEnumerable и IEnumerable<T> явно
// выбираем IEnumerable<T>
if (genericInterfaceCount == 1)
return filteredInterfaces.First(item => item.is_generic_type_instance);
// явно реализует IEnumerable
else
return IEnumType;
case 1:
var method = methods[0];
if (!method.return_value_type.is_generic_type_instance)
return IEnumType;
else
return IEnumTypedType.get_instance(method.return_value_type.instance_params);
default:
// в типе наследнике перекрыли GetEnumerator
// невозможно точно выбрать метод
return null;
}
}
public bool FindIEnumerableElementType(type_node tn, ref type_node elem_type, out bool sys_coll_ienum)
{
sys_coll_ienum = false;
var IEnstring = "System.Collections.IEnumerable";
compiled_type_node ctn = compiled_type_node.get_type_node(NetHelper.NetHelper.FindType(IEnstring));
if (tn is compiled_type_node || tn is compiled_generic_instance_type_node) // Если этот тип зашит в .NET
// IEnumerable<integer>, Range(1,10), Dictionary<string,integer>: tn = compiled_type_node
// IEnumerable<T>: tn = compiled_generic_instance_type_node
// FibGen = class(IEnumerable,IEnumerator): tn = common_type_node, en = compiled_type_node
// array of Person: tn = common_type_node
// поиск в типе, объявленном в другой сборке
// и тип и типоаргументы объявлены в другой сборке
// Dictionary<string, integer>: tn = compiled_type_node
// IEnumerable<integer>: tn = compiled_type_node
//
// тип объявлен в другой сборке, а типоаргументы в коде
// Dictionary<string, MyType>: tn = compiled_generic_instance_type_node
// IEnumerable<T1>: tn = compiled_generic_instance_type_node (например в generic подпрогамме)
if (tn is compiled_type_node || tn is compiled_generic_instance_type_node)
{
compiled_type_node orig;
if (tn is compiled_type_node)
orig = tn as compiled_type_node;
else orig = (tn as compiled_generic_instance_type_node).original_generic as compiled_type_node;
//var pars = tn.instance_params;
System.Type ct = orig.compiled_type;
Type r;
var IEnTstring = "System.Collections.Generic.IEnumerable`1";
//if (ct.ToString().StartsWith(IEnTstring)) // SSM Ошибка!!! IEnumerable`1[] тоже начинается с IEnumerable`1, но это массив!
if (ct.Name.Equals("IEnumerable`1"))
r = ct;
else
r = ct.GetInterface(IEnTstring);
if (r != null)
else
orig = (tn as compiled_generic_instance_type_node).original_generic as compiled_type_node;
// непосредственно тип, от котоого разворачивается foreach
Type ct = orig.compiled_type;
var isEnumeratedType = typeof(IEnumerable).IsAssignableFrom(ct);
if (!isEnumeratedType)
return false;
// для массива
if (tn.element_type != null)
{
Type arg1 = r.GetGenericArguments().First(); // тип параметра IEnumerable
var str = arg1.GetGenericArguments().Count();
if (tn is compiled_type_node)
elem_type = tn.element_type;
if (ct.GetArrayRank() > 1)
sys_coll_ienum = true;
return true;
}
// в иных случаях ищем подходящий интерфейс
var desiredInterface = FindIEnumerableInterfaceInCompiledType(ct);
if (desiredInterface == null)
return false;
// обнаружен IEnumerable
if (!desiredInterface.IsGenericType)
{
elem_type = SystemLibrary.SystemLibrary.object_type;
sys_coll_ienum = true;
return true;
}
var foundElementType = desiredInterface.GetGenericArguments().First();
if (tn is compiled_type_node)
elem_type = compiled_type_node.get_type_node(foundElementType);
else
{
// для "tn is compiled_generic_instance_type_node" необходимо восстановить стёртые типы
// orig.instance_params содержит имена типоаргументов при объявлении
// tn.instance_params содержит фактические типоаргументы
// остаётся найти нужный индекс в orig и взять аргумент из tn
if (foundElementType.IsGenericParameter)
{
elem_type = compiled_type_node.get_type_node(arg1);
var ind = orig.instance_params.FindIndex(item => item.name == foundElementType.Name);
elem_type = tn.instance_params[ind];
}
else
{
if (arg1.GetGenericArguments().Count()>0)
{
elem_type = compiled_type_node.get_type_node(arg1.GetGenericTypeDefinition());
elem_type = elem_type.get_instance(tn.instance_params); // SSM 19/07/15 - работает!!!
}
else
{
var ip = tn.instance_params;
// значит элемент последовательности сам является generic типом
// нужно сопоставить все типоаргументы из orig типоаргументам из tn
// нельзя просто брать все, тк их количество может отличаться
var IGrTstring = "System.Linq.IGrouping`2";
if (ct.ToString().StartsWith(IGrTstring))
elem_type = ip[1];
else
{
var ln = tn.ImplementingInterfaces;
elem_type = null;
foreach (var x in ln)
{
var xctn = x as compiled_generic_instance_type_node;
if (xctn != null && xctn.name.StartsWith("IEnumerable<")) // Немного грубовато.
{
elem_type = xctn.instance_params[0];
break;
}
}
if (ct.Name == "ValueCollection" && ct.IsNested && ct.GetGenericArguments().Length == 2)
{
elem_type = ip[1];
}
else if (elem_type == null)
elem_type = ip[0];
}
//var Tname = ip[0].name;
//elem_type = convert_strong(new SyntaxTree.named_type_reference(Tname, _foreach_stmt.in_what.source_context));
}
var declaredTypes = foundElementType
.GetGenericArguments()
.Select(item=> item.Name)
.ToArray();
//elem_type.instance_params = tn.instance_params;
//var ip = tn.instance_params;
//var Tname = new string(tn.name.SkipWhile(c => c != '<').Skip(1).TakeWhile(c => c != ',' && c != '>').ToArray());
//var Tname = ip[0].name;
//var Tname = "System.Collections.Generic.KeyValuePair'2"; // <integer,TClass>
//elem_type = convert_strong(new SyntaxTree.named_type_reference(Tname, _foreach_stmt.in_what.source_context));
}
return true;
}
else
{
if (tn.element_type != null) // значит, это массив любой размерности - 02.02.16 SSM - еще может быть множество set of T - 22.02.16 SSM
{
elem_type = tn.element_type;
return true;
}
var actualTypes = tn.instance_params
.Where((item, i) => declaredTypes.Contains(orig.instance_params[i].name))
.ToList();
var ttt = tn.ImplementingInterfaces;
foreach (SemanticTree.ITypeNode itn in tn.ImplementingInterfaces)
{
if (itn == ctn)
{
elem_type = SystemLibrary.SystemLibrary.object_type;
sys_coll_ienum = true;
return true;
}
elem_type = compiled_type_node.get_type_node( foundElementType.GetGenericTypeDefinition() );
elem_type = elem_type.get_instance(actualTypes);
}
}
}
/*else if (tn is compiled_generic_instance_type_node)
{
var g = tn as compiled_generic_instance_type_node;
var og = g.original_generic;
var tt = og.ImplementingInterfaces;
if (_foreach_stmt.type_name != null && _foreach_stmt.type_name.GetType()!=typeof(SyntaxTree.no_type_foreach))
elem_type = convert_strong(_foreach_stmt.type_name);
else
{
var fn = tn.full_name;
}
return true;
} */
else // если мы самостоятельно определяем этот тип - можно реализовать в PascalABC.NET только IEnumerable. // Сейчас уже можно!!!!!
// Попытка реализовать IEnumerable<T> натыкается на необходимость определять GetEnumerator, возвращающий IEnumerator и IEnumerator<T>
{
if (tn == null || tn is null_type_node || tn.ImplementingInterfaces == null)
{
if (tn != null && tn.base_type != null)
return FindIEnumerableElementType(tn.base_type, ref elem_type, out sys_coll_ienum);
return false;
}
}
if (tn.element_type != null && tn.type_special_kind != SemanticTree.type_special_kind.typed_file) // еще может быть множество set of T - 22.02.16 SSM
// поиск в типе, объявленном в коде
// также сюда попадают set of T даже если они подтягиваются из библиотеки
else
{
// массивы и set of T
if (tn.element_type != null && tn.type_special_kind != SemanticTree.type_special_kind.typed_file)
{
elem_type = tn.element_type;
if ((tn as common_type_node).rank != 1)
sys_coll_ienum = true;
return true;
}
foreach (SemanticTree.ITypeNode itn in tn.ImplementingInterfaces) // Ищем интерфейс IEnumerable<T> и возвращаем T в качестве elem_type
{
if (itn is compiled_generic_instance_type_node)
{
var itnc = (itn as compiled_generic_instance_type_node);
var tt = (itnc.original_generic as compiled_type_node).compiled_type;
if (tt == typeof(System.Collections.Generic.IEnumerable<>))
{
elem_type = itnc.generic_parameters[0] as common_type_node;
return true;
}
}
}
var desiredInterface = FindIEnumerableInterfaceInTypeNode(tn);
foreach (SemanticTree.ITypeNode itn in tn.ImplementingInterfaces) // если не нашли - ищем интерфейс IEnumerable и возвращаем object в качестве elem_type
if (desiredInterface == null)
return false;
if (!desiredInterface.is_generic_type_instance)
{
if (itn is compiled_type_node) // Именно этот кусок отвечает за IEnumerable<integer> и мы его закомментировали
{
var itnc = (itn as compiled_type_node).compiled_type; /* SSM раскомментировал 15.05.2020 - пришла пора*/
if (itnc.IsGenericType)
{
var my = itnc.GetGenericTypeDefinition();// = typeof(System.Collections.Generic.IEnumerable<>)
if (my == typeof(System.Collections.Generic.IEnumerable<>))
{
var aarg1 = itnc.GetGenericArguments().First();
elem_type = compiled_type_node.get_type_node(aarg1);
return true;
}
}
else /* end SSM раскомментировал 15.05.2020 - пришла пора*/
if (itn == ctn)
{
elem_type = SystemLibrary.SystemLibrary.object_type;
sys_coll_ienum = true;
return true;
}
}
elem_type = SystemLibrary.SystemLibrary.object_type;
sys_coll_ienum = true;
}
else
elem_type = desiredInterface.instance_params[0];
return true;
}
if (tn != null && tn.base_type != null)
return FindIEnumerableElementType(tn.base_type, ref elem_type, out sys_coll_ienum);
return false;
}
private int GenIdNum = 0;

View file

@ -1754,12 +1754,23 @@ namespace PascalABCCompiler.TreeRealization
private var_definition_node _ident;
private expression_node _in_what;
private statement_node _what_do;
private type_node _element_type;
private bool _is_generic;
public foreach_node(var_definition_node _ident, expression_node _in_what, statement_node _what_do, location loc):base(loc)
public foreach_node(
var_definition_node _ident,
expression_node _in_what,
statement_node _what_do,
type_node _element_type,
bool _is_generic,
location loc
):base(loc)
{
this._ident = _ident;
this._in_what = _in_what;
this._what_do = _what_do;
this._element_type = _element_type;
this._is_generic = _is_generic;
}
public var_definition_node ident
@ -1798,6 +1809,18 @@ namespace PascalABCCompiler.TreeRealization
}
}
public type_node element_type
{
get { return _element_type; }
set { _element_type = value; }
}
public bool is_generic
{
get { return _is_generic; }
set { _is_generic = value; }
}
public SemanticTree.IStatementNode Body
{
get
@ -1821,6 +1844,17 @@ namespace PascalABCCompiler.TreeRealization
return _ident;
}
}
public SemanticTree.ITypeNode ElementType
{
get { return _element_type; }
}
public bool IsGeneric
{
get { return _is_generic; }
}
/// <summary>
/// Тип узла.
/// </summary>