diff --git a/Compiler/PCU/PCUFileFormatVersion.cs b/Compiler/PCU/PCUFileFormatVersion.cs index df9533567..0bdbc719a 100644 --- a/Compiler/PCU/PCUFileFormatVersion.cs +++ b/Compiler/PCU/PCUFileFormatVersion.cs @@ -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; } } diff --git a/Compiler/PCU/PCUReader.cs b/Compiler/PCU/PCUReader.cs index aa8199172..f53de45b7 100644 --- a/Compiler/PCU/PCUReader.cs +++ b/Compiler/PCU/PCUReader.cs @@ -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() diff --git a/Compiler/PCU/PCUWriter.cs b/Compiler/PCU/PCUWriter.cs index e75c2e3e9..70a0c25bf 100644 --- a/Compiler/PCU/PCUWriter.cs +++ b/Compiler/PCU/PCUWriter.cs @@ -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) diff --git a/NETGenerator/Helpers.cs b/NETGenerator/Helpers.cs index f47f9ed49..411f80c17 100644 --- a/NETGenerator/Helpers.cs +++ b/NETGenerator/Helpers.cs @@ -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; diff --git a/NETGenerator/NETGenerator.cs b/NETGenerator/NETGenerator.cs index cbd3a9fd1..58187702b 100644 --- a/NETGenerator/NETGenerator.cs +++ b/NETGenerator/NETGenerator.cs @@ -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, 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 + return_type = enumer_mi.ReturnType + .GetGenericTypeDefinition() + .MakeGenericType(elementType); } + // для полностью скомпилированных типов TypeBuilder не требуется + // IEnumerable, 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++); diff --git a/SemanticTree/SemanticTree.cs b/SemanticTree/SemanticTree.cs index 29dcd0b91..94ccc68ff 100644 --- a/SemanticTree/SemanticTree.cs +++ b/SemanticTree/SemanticTree.cs @@ -2350,6 +2350,16 @@ namespace PascalABCCompiler.SemanticTree { get; } + + ITypeNode ElementType + { + get; + } + + bool IsGeneric + { + get; + } } public interface ILockStatement : IStatementNode, ILocated diff --git a/TestSuite/errors/err0540_foreach_resolver.pas b/TestSuite/errors/err0540_foreach_resolver.pas new file mode 100644 index 000000000..f57f7c1b2 --- /dev/null +++ b/TestSuite/errors/err0540_foreach_resolver.pas @@ -0,0 +1,20 @@ +//!Невозможно выполнить оператор foreach или yield sequence по выражению типа c1 + +uses System.Collections, System.Collections.Generic; + +type + c1 = class (IEnumerable, IEnumerable) + public + function IEnumerable.GetEnumerator: IEnumerator; + begin end; + + function IEnumerable.GetEnumerator: IEnumerator; + begin end; + + function IEnumerable.GetEnumerator: IEnumerator; + begin end; + end; + +begin + foreach var item in new c1 do ; +end. \ No newline at end of file diff --git a/TestSuite/foreach_resolver1.pas b/TestSuite/foreach_resolver1.pas new file mode 100644 index 000000000..c61937fea --- /dev/null +++ b/TestSuite/foreach_resolver1.pas @@ -0,0 +1,48 @@ +{$reference 'foreach_resolver_lib.dll'} + +type + c0 = class + procedure pr:= exit; + end; + +begin + var v1:= new c1; + 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. \ No newline at end of file diff --git a/TestSuite/foreach_resolver2.pas b/TestSuite/foreach_resolver2.pas new file mode 100644 index 000000000..70333207e --- /dev/null +++ b/TestSuite/foreach_resolver2.pas @@ -0,0 +1,67 @@ +uses System.Collections, System.Collections.Generic; + +type + c1 = class (IEnumerable) + public + + function IEnumerable.GetEnumerator: IEnumerator; + begin + Assert(false); + end; + + function GetEnumerator: IEnumerator; + begin + result:= Seq&().GetEnumerator; + end; + end; + + c2 = class (IEnumerable) + public + function GetEnumerator: IEnumerator; + begin + result:= Seq&().GetEnumerator; + end; + + function IEnumerable.GetEnumerator: IEnumerator; + begin + Assert(false); + end; + end; + + c3 = class (IEnumerable, IEnumerable) + public + function GetEnumerator: IEnumerator; + begin + result:= Seq&().GetEnumerator; + end; + + function IEnumerable.GetEnumerator: IEnumerator; + begin + Assert(false); + end; + + function IEnumerable.GetEnumerator: IEnumerator; + begin + Assert(false); + end; + end; + + c4 = class (c2) + + end; + +begin + + var v1:= new c1; + foreach var item in v1 do ; + + var v2:= new c2; + 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. \ No newline at end of file diff --git a/TestSuite/foreach_resolver_lib.dll b/TestSuite/foreach_resolver_lib.dll new file mode 100644 index 000000000..e56056d11 Binary files /dev/null and b/TestSuite/foreach_resolver_lib.dll differ diff --git a/TestSuite/foreach_resolver_lib.pas b/TestSuite/foreach_resolver_lib.pas new file mode 100644 index 000000000..3f629a79e --- /dev/null +++ b/TestSuite/foreach_resolver_lib.pas @@ -0,0 +1,58 @@ +library foreach_resolver_lib; + uses System.Collections, System.Collections.Generic; + + var check:= false; + + type + c1 = class (IEnumerable, IEnumerable) + public + _element: T2; + + function GetEnumerator: IEnumerator; + begin + check:= true; + result:= lst( _element ).GetEnumerator; + end; + + function IEnumerable.GetEnumerator: IEnumerator; + begin end; + + function IEnumerable.GetEnumerator: IEnumerator; + begin end; + end; + + c2 = class (IEnumerable, IEnumerable) + public + _element: string; + + function GetEnumerator: IEnumerator; + begin + check:= true; + result:= lst(_element).GetEnumerator; + end; + + function IEnumerable.GetEnumerator: IEnumerator; + begin end; + + function IEnumerable.GetEnumerator: IEnumerator; + begin end; + end; + + c3 = class (IEnumerable) + public + _element: object; + + function IEnumerable.GetEnumerator: IEnumerator; + begin end; + + function GetEnumerator: IEnumerator; + begin + check:= true; + result:= |_element|.GetEnumerator; + end; + end; + + c4 = class (c2) end; + + c5 = class (c1) end; +end. \ No newline at end of file diff --git a/TreeConverter/LambdaExpressions/Closure/CapturedVariablesTreeBuilder.cs b/TreeConverter/LambdaExpressions/Closure/CapturedVariablesTreeBuilder.cs index 545daadf7..b4ed54b74 100644 --- a/TreeConverter/LambdaExpressions/Closure/CapturedVariablesTreeBuilder.cs +++ b/TreeConverter/LambdaExpressions/Closure/CapturedVariablesTreeBuilder.cs @@ -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); diff --git a/TreeConverter/TreeConversion/SyntaxTreeVisitorNodes/Foreach.cs b/TreeConverter/TreeConversion/SyntaxTreeVisitorNodes/Foreach.cs index 0a30a594d..a8f25cd54 100644 --- a/TreeConverter/TreeConversion/SyntaxTreeVisitorNodes/Foreach.cs +++ b/TreeConverter/TreeConversion/SyntaxTreeVisitorNodes/Foreach.cs @@ -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) { diff --git a/TreeConverter/TreeConversion/syntax_tree_visitor.cs b/TreeConverter/TreeConversion/syntax_tree_visitor.cs index 6da486b2a..b2f44a5c2 100644 --- a/TreeConverter/TreeConversion/syntax_tree_visitor.cs +++ b/TreeConverter/TreeConversion/syntax_tree_visitor.cs @@ -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 то берём его + 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 + // невозможно выбрать. требуется явное приведение + 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 + // невозможно выбрать. требуется явное приведение + if (genericInterfaceCount > 1) + return null; + // тип реализует и IEnumerable и IEnumerable явно + // выбираем IEnumerable + 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); + } + + /// + /// Собирает интерфейсы, реализованные типом и его предками + /// + private List CollectInterfacesFromTypeNode(type_node node) + { + var result = new List(); + result.AddRange(node.ImplementingInterfaces); + + if (node.base_type != null) + { + var interfInBase = CollectInterfacesFromTypeNode(node.base_type); + result.AddRange(interfInBase); + } + + return result.Cast().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 + // невозможно выбрать. требуется явное приведение + 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 + // невозможно выбрать. требуется явное приведение + if (genericInterfaceCount > 1) + return null; + // тип реализует и IEnumerable и IEnumerable явно + // выбираем IEnumerable + 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, Range(1,10), Dictionary: tn = compiled_type_node - // IEnumerable: 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: tn = compiled_type_node + // IEnumerable: tn = compiled_type_node + // + // тип объявлен в другой сборке, а типоаргументы в коде + // Dictionary: tn = compiled_generic_instance_type_node + // IEnumerable: 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"; // - //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 натыкается на необходимость определять GetEnumerator, возвращающий IEnumerator и IEnumerator - { - 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 в качестве 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 и мы его закомментировали - { - 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; diff --git a/TreeConverter/TreeRealization/statements.cs b/TreeConverter/TreeRealization/statements.cs index 4c57148b3..e4666a416 100644 --- a/TreeConverter/TreeRealization/statements.cs +++ b/TreeConverter/TreeRealization/statements.cs @@ -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; } + } + /// /// Тип узла. ///