From 4db76663d126f444269f96d05e7cb97b6c827b7a Mon Sep 17 00:00:00 2001 From: Ivan Bondarev Date: Wed, 6 Jan 2021 14:20:07 +0100 Subject: [PATCH] #2328 --- TestSuite/lambdas15.pas | 13 + TestSuite/lambdas_generics4.pas | 4 +- TestSuite/lambdas_generics6.pas | 4 +- .../TreeConversion/syntax_tree_visitor.cs | 386 +++++++++--------- 4 files changed, 214 insertions(+), 193 deletions(-) create mode 100644 TestSuite/lambdas15.pas diff --git a/TestSuite/lambdas15.pas b/TestSuite/lambdas15.pas new file mode 100644 index 000000000..a65169916 --- /dev/null +++ b/TestSuite/lambdas15.pas @@ -0,0 +1,13 @@ +begin + +var i := 0; +// Обязательно p0 - процедурная переменная, с методом или глобальной подпрограммой не воспроизводится +// Обязательно принимать в p хотя бы 1 параметр +var p0 := procedure(p: byte->()) -> p(2); + +// Обязательно вызвать передав лямбду +p0(b->Inc(i)); + +assert(i = 1); + +end. \ No newline at end of file diff --git a/TestSuite/lambdas_generics4.pas b/TestSuite/lambdas_generics4.pas index b584abbe1..dee96782b 100644 --- a/TestSuite/lambdas_generics4.pas +++ b/TestSuite/lambdas_generics4.pas @@ -1,8 +1,8 @@ -uses System, System.Collections.Generic; +uses System, System.Collections.Generic; procedure p (a: X; b: Action>>; c: Y); begin - writeln(b.GetType()); + assert(b.GetType().ToString() = 'System.Action`1[System.Collections.Generic.IEnumerable`1[System.Collections.Generic.Dictionary`2[System.Int32,System.Int32]]]'); end; begin diff --git a/TestSuite/lambdas_generics6.pas b/TestSuite/lambdas_generics6.pas index 9efea6911..37c9a46eb 100644 --- a/TestSuite/lambdas_generics6.pas +++ b/TestSuite/lambdas_generics6.pas @@ -1,8 +1,8 @@ -uses System, System.Collections.Generic; +uses System, System.Collections.Generic; procedure p (b: Func, X>; c: Y); begin - writeln(b.GetType()); + assert(b.GetType().ToString() = 'System.Func`2[System.Collections.Generic.IEnumerable`1[System.Int32],System.Collections.Generic.IEnumerable`1[System.Int32]]'); end; begin diff --git a/TreeConverter/TreeConversion/syntax_tree_visitor.cs b/TreeConverter/TreeConversion/syntax_tree_visitor.cs index 3b4daf4a0..d0dec58cc 100644 --- a/TreeConverter/TreeConversion/syntax_tree_visitor.cs +++ b/TreeConverter/TreeConversion/syntax_tree_visitor.cs @@ -7175,6 +7175,202 @@ namespace PascalABCCompiler.TreeConverter } else { + #region Если встретились лямбды в фактических параметрах, то выбираем нужную функцию из перегруженных, выводим типы, отмечаем флаг в лямбдах, говорящий о том, что мы их реально обходим + //lroman// + if (lambdas_are_in_parameters) + { + LambdaHelper.processingLambdaParametersForTypeInference++; + var sil2 = sil; + location mcloc = get_location(_method_call); + if (iwt != null) + { + sil2 = get_generic_functions(sil, true, mcloc); + sil2 = get_function_instances(sil2, iwt.template_params.params_list, id.name, mcloc, sil2.Count() <= 1); + } + // SSM 21.05.14 - попытка обработать перегруженные функции с параметрами-лямбдами с различными возвращаемыми значениями + List spf = null; + try + { + function_node fn = convertion_data_and_alghoritms.select_function(exprs, sil2, subloc2, syntax_nodes_parameters); + int exprCounter = 0; + foreach (SyntaxTree.expression en in _method_call.parameters.expressions) + { + if (!(en is SyntaxTree.function_lambda_definition)) + { + exprCounter++; + continue; + } + else + { + var enLambda = (SyntaxTree.function_lambda_definition)en; + LambdaHelper.InferTypesFromVarStmt(fn.parameters[exprCounter].type, enLambda, this); + enLambda.lambda_visit_mode = LambdaVisitMode.VisitForAdvancedMethodCallProcessing; + exprs[exprCounter] = convert_strong(en); + enLambda.lambda_visit_mode = LambdaVisitMode.VisitForInitialMethodCallProcessing; + exprCounter++; + } + } + } + catch (SeveralFunctionsCanBeCalled sf) + { + spf = sf.set_of_possible_functions; // Возможны несколько перегруженных версий - надо выводить дальше в надежде что какие-то уйдут и останется одна + } + + Exception lastmultex = null; + if (spf != null) // пытаемся инстанцировать одну за другой и ошибки гасим try + { // exprs - глобальная, поэтому надо копировать + int spfnum = -1; // первый номер правильно инстанцированной. Если потом встретился второй, то тоже ошибка + // SSM 4.08.15. Сейчас меняю эту логику. Если будет много кандидатов, но ровно один с совпадающим типом возвращаемого значения, то его и надо выбирать. + // не забыть, что аналогичный код есть в create_constructor_call!!!!!!! И еще выше по коду!!! кошмар!!! + int GoodVersionsCount = 0; + int GoodVersionsCountWithSameResType = 0; + for (int i = 0; i < spf.Count; i++) // цикл по версиям + { + function_node fn = spf[i]; + try // внутренний try регенерирует исключение, а этот гасит + { + int exprCounter = 0; + expressions_list exprs1 = new expressions_list(); + exprs1.AddRange(exprs); // сделали копию + + foreach (SyntaxTree.expression en in _method_call.parameters.expressions) + { + if (!(en is SyntaxTree.function_lambda_definition)) + { + exprCounter++; + continue; + } + else + { + var fld = en as SyntaxTree.function_lambda_definition; + + var lambdaName = fld.lambda_name; //lroman Сохранять имя необходимо + var fl = fld.lambda_visit_mode; + + // запомнили типы параметров лямбды - SSM + var cnt = fld.formal_parameters?.params_list?.Count ?? 0; // SSM 22.06.19 + object[] realparamstype = new object[cnt]; // здесь хранятся выведенные типы лямбд или null если типы явно заданы + for (var k = 0; k < cnt; k++) + { + var laminftypeK = fld.formal_parameters.params_list[k].vars_type as SyntaxTree.lambda_inferred_type; + if (laminftypeK == null) + realparamstype[k] = null; + else realparamstype[k] = laminftypeK.real_type; + } + + // запоминаем реальный тип возвращаемого значения если он не указан явно (это должен быть any_type или null если он указан явно) - он может измениться при следующем вызове, поэтому мы его восстановим + var restype = fld.return_type as SyntaxTree.lambda_inferred_type; + object realrestype = null; + if (restype != null) + realrestype = restype.real_type; + + LambdaHelper.InferTypesFromVarStmt(fn.parameters[exprCounter].type, fld, this); + fld.lambda_visit_mode = LambdaVisitMode.VisitForAdvancedMethodCallProcessing; //lroman + fld.lambda_name = LambdaHelper.GetAuxiliaryLambdaName(lambdaName); // поправляю имя. Думаю, назад возвращать не надо. ПРОВЕРИТЬ! + + //contextChanger.SaveContextAndUpToNearestDefSect(); + try + { + exprs1[exprCounter] = convert_strong(en); + + // SSM 7/08/15 + + type_node resexprtype = fld.RealSemTypeOfResExpr as type_node; + type_node resformaltype = fld.RealSemTypeOfResult as type_node; + var bbb = resexprtype == resformaltype; // только в одном случае должно быть true - эту версию и надо выбирать. Если в нескольких, то неоднозначность + if (bbb) + { + GoodVersionsCountWithSameResType += 1; + spfnum = i; // здесь запоминаем индекс потому что он точно подойдет. Тогда ниже он запоминаться не будет. + } + + /*var tt = fn.parameters[exprCounter].type as compiled_type_node; + if (tt != null && tt.compiled_type.FullName.ToLower().StartsWith("system.func")) + { + resformaltype = tt.instance_params[tt.instance_params.Count - 1]; // Последний параметр в записи Func - тип возвращаемого значения + var bbb = resexprtype == resformaltype; // только в одном случае должно быть true - эту версию и надо выбирать. Если в нескольких, то неоднозначность + if (bbb) + { + GoodVersionsCountWithSameResType += 1; + spfnum = i; // здесь запоминаем индекс потому что он точно подойдет. Тогда ниже он запоминаться не будет. + } + }*/ + } + catch + { + throw; + } + finally + { + LambdaHelper.RemoveLambdaInfoFromCompilationContext(context, en as function_lambda_definition); + // восстанавливаем сохраненный тип возвращаемого значения + if (restype != null) + restype.real_type = realrestype; + // восстанавливаем сохраненные типы параметров лямбды, которые не были заданы явно + for (var k = 0; k < (fld.formal_parameters?.params_list?.Count ?? 0); k++) + { + var laminftypeK = fld.formal_parameters.params_list[k].vars_type as SyntaxTree.lambda_inferred_type; + if (laminftypeK != null) + laminftypeK.real_type = realparamstype[k]; + } + + fld.lambda_name = lambdaName; //lroman Восстанавливаем имена + fld.lambda_visit_mode = fl; + } + + //contextChanger.RestoreCurrentContext(); + exprCounter++; + } + } + /*if (spfnum >= 0) // два удачных инстанцирования - плохо. Может, одно - с более близким типом возвращаемого значения, тогда это плохо - надо доделать, но пока так + { + spfnum = -2; + break; + }*/ + + if (GoodVersionsCountWithSameResType == 0) + spfnum = i; // здесь запоминаем индекс только если нет подошедших, совпадающих по типу возвращаемого значения + GoodVersionsCount += 1; + for (int j = 0; j < exprs.Count; j++) // копируем назад если всё хорошо + exprs[j] = exprs1[j]; + } + catch (Exception e) + { + // если сюда попали, значит, не вывели типы в лямбде и надо эту инстанцию пропускать + //contextChanger.RestoreCurrentContext(); + lastmultex = e; + } + } //--------------- конец цикла по версиям + + if (GoodVersionsCount > 1 && GoodVersionsCountWithSameResType != 1) // подошло много, но не было ровно одной с совпадающим типом возвращаемого значения + throw new SeveralFunctionsCanBeCalled(subloc2, spf); + if (GoodVersionsCount == 0) // было много, но ни одна не подошла из-за лямбд + { + throw lastmultex; + //throw new NoFunctionWithSameArguments(subloc2, false); + } + + var kk = 0; + foreach (SyntaxTree.expression en in _method_call.parameters.expressions) //lroman окончательно подставить типы в лямбды + { + if (!(en is SyntaxTree.function_lambda_definition)) + { + kk++; + continue; + } + else + { + LambdaHelper.InferTypesFromVarStmt(spf[spfnum].parameters[kk].type, en as SyntaxTree.function_lambda_definition, this); + exprs[kk] = convert_strong(en); + kk++; + } + } + } + // SSM 21.05.14 end + LambdaHelper.processingLambdaParametersForTypeInference--; + } + //lroman// + #endregion if (exp2 == null) { location mcloc = get_location(_method_call); @@ -7184,195 +7380,7 @@ namespace PascalABCCompiler.TreeConverter sil = get_function_instances(sil, iwt.template_params.params_list, id.name, mcloc, sil.Count() <= 1); } - #region Если встретились лямбды в фактических параметрах, то выбираем нужную функцию из перегруженных, выводим типы, отмечаем флаг в лямбдах, говорящий о том, что мы их реально обходим - //lroman// - if (lambdas_are_in_parameters) - { - LambdaHelper.processingLambdaParametersForTypeInference++; - // SSM 21.05.14 - попытка обработать перегруженные функции с параметрами-лямбдами с различными возвращаемыми значениями - List spf = null; - try - { - function_node fn = convertion_data_and_alghoritms.select_function(exprs, sil, subloc2, syntax_nodes_parameters); - int exprCounter = 0; - foreach (SyntaxTree.expression en in _method_call.parameters.expressions) - { - if (!(en is SyntaxTree.function_lambda_definition)) - { - exprCounter++; - continue; - } - else - { - var enLambda = (SyntaxTree.function_lambda_definition)en; - LambdaHelper.InferTypesFromVarStmt(fn.parameters[exprCounter].type, enLambda, this); - enLambda.lambda_visit_mode = LambdaVisitMode.VisitForAdvancedMethodCallProcessing; - exprs[exprCounter] = convert_strong(en); - enLambda.lambda_visit_mode = LambdaVisitMode.VisitForInitialMethodCallProcessing; - exprCounter++; - } - } - } - catch (SeveralFunctionsCanBeCalled sf) - { - spf = sf.set_of_possible_functions; // Возможны несколько перегруженных версий - надо выводить дальше в надежде что какие-то уйдут и останется одна - } - - Exception lastmultex = null; - if (spf != null) // пытаемся инстанцировать одну за другой и ошибки гасим try - { // exprs - глобальная, поэтому надо копировать - int spfnum = -1; // первый номер правильно инстанцированной. Если потом встретился второй, то тоже ошибка - // SSM 4.08.15. Сейчас меняю эту логику. Если будет много кандидатов, но ровно один с совпадающим типом возвращаемого значения, то его и надо выбирать. - // не забыть, что аналогичный код есть в create_constructor_call!!!!!!! И еще выше по коду!!! кошмар!!! - int GoodVersionsCount = 0; - int GoodVersionsCountWithSameResType = 0; - for (int i = 0; i < spf.Count; i++) // цикл по версиям - { - function_node fn = spf[i]; - try // внутренний try регенерирует исключение, а этот гасит - { - int exprCounter = 0; - expressions_list exprs1 = new expressions_list(); - exprs1.AddRange(exprs); // сделали копию - - foreach (SyntaxTree.expression en in _method_call.parameters.expressions) - { - if (!(en is SyntaxTree.function_lambda_definition)) - { - exprCounter++; - continue; - } - else - { - var fld = en as SyntaxTree.function_lambda_definition; - - var lambdaName = fld.lambda_name; //lroman Сохранять имя необходимо - var fl = fld.lambda_visit_mode; - - // запомнили типы параметров лямбды - SSM - var cnt = fld.formal_parameters?.params_list?.Count ?? 0; // SSM 22.06.19 - object[] realparamstype = new object[cnt]; // здесь хранятся выведенные типы лямбд или null если типы явно заданы - for (var k = 0; k < cnt; k++) - { - var laminftypeK = fld.formal_parameters.params_list[k].vars_type as SyntaxTree.lambda_inferred_type; - if (laminftypeK == null) - realparamstype[k] = null; - else realparamstype[k] = laminftypeK.real_type; - } - - // запоминаем реальный тип возвращаемого значения если он не указан явно (это должен быть any_type или null если он указан явно) - он может измениться при следующем вызове, поэтому мы его восстановим - var restype = fld.return_type as SyntaxTree.lambda_inferred_type; - object realrestype = null; - if (restype != null) - realrestype = restype.real_type; - - LambdaHelper.InferTypesFromVarStmt(fn.parameters[exprCounter].type, fld, this); - fld.lambda_visit_mode = LambdaVisitMode.VisitForAdvancedMethodCallProcessing; //lroman - fld.lambda_name = LambdaHelper.GetAuxiliaryLambdaName(lambdaName); // поправляю имя. Думаю, назад возвращать не надо. ПРОВЕРИТЬ! - - //contextChanger.SaveContextAndUpToNearestDefSect(); - try - { - exprs1[exprCounter] = convert_strong(en); - - // SSM 7/08/15 - - type_node resexprtype = fld.RealSemTypeOfResExpr as type_node; - type_node resformaltype = fld.RealSemTypeOfResult as type_node; - var bbb = resexprtype == resformaltype; // только в одном случае должно быть true - эту версию и надо выбирать. Если в нескольких, то неоднозначность - if (bbb) - { - GoodVersionsCountWithSameResType += 1; - spfnum = i; // здесь запоминаем индекс потому что он точно подойдет. Тогда ниже он запоминаться не будет. - } - - /*var tt = fn.parameters[exprCounter].type as compiled_type_node; - if (tt != null && tt.compiled_type.FullName.ToLower().StartsWith("system.func")) - { - resformaltype = tt.instance_params[tt.instance_params.Count - 1]; // Последний параметр в записи Func - тип возвращаемого значения - var bbb = resexprtype == resformaltype; // только в одном случае должно быть true - эту версию и надо выбирать. Если в нескольких, то неоднозначность - if (bbb) - { - GoodVersionsCountWithSameResType += 1; - spfnum = i; // здесь запоминаем индекс потому что он точно подойдет. Тогда ниже он запоминаться не будет. - } - }*/ - } - catch - { - throw; - } - finally - { - LambdaHelper.RemoveLambdaInfoFromCompilationContext(context, en as function_lambda_definition); - // восстанавливаем сохраненный тип возвращаемого значения - if (restype != null) - restype.real_type = realrestype; - // восстанавливаем сохраненные типы параметров лямбды, которые не были заданы явно - for (var k = 0; k < (fld.formal_parameters?.params_list?.Count ?? 0); k++) - { - var laminftypeK = fld.formal_parameters.params_list[k].vars_type as SyntaxTree.lambda_inferred_type; - if (laminftypeK != null) - laminftypeK.real_type = realparamstype[k]; - } - - fld.lambda_name = lambdaName; //lroman Восстанавливаем имена - fld.lambda_visit_mode = fl; - } - - //contextChanger.RestoreCurrentContext(); - exprCounter++; - } - } - /*if (spfnum >= 0) // два удачных инстанцирования - плохо. Может, одно - с более близким типом возвращаемого значения, тогда это плохо - надо доделать, но пока так - { - spfnum = -2; - break; - }*/ - - if (GoodVersionsCountWithSameResType == 0) - spfnum = i; // здесь запоминаем индекс только если нет подошедших, совпадающих по типу возвращаемого значения - GoodVersionsCount += 1; - for (int j = 0; j < exprs.Count; j++) // копируем назад если всё хорошо - exprs[j] = exprs1[j]; - } - catch (Exception e) - { - // если сюда попали, значит, не вывели типы в лямбде и надо эту инстанцию пропускать - //contextChanger.RestoreCurrentContext(); - lastmultex = e; - } - } //--------------- конец цикла по версиям - - if (GoodVersionsCount > 1 && GoodVersionsCountWithSameResType != 1) // подошло много, но не было ровно одной с совпадающим типом возвращаемого значения - throw new SeveralFunctionsCanBeCalled(subloc2, spf); - if (GoodVersionsCount == 0) // было много, но ни одна не подошла из-за лямбд - { - throw lastmultex; - //throw new NoFunctionWithSameArguments(subloc2, false); - } - - var kk = 0; - foreach (SyntaxTree.expression en in _method_call.parameters.expressions) //lroman окончательно подставить типы в лямбды - { - if (!(en is SyntaxTree.function_lambda_definition)) - { - kk++; - continue; - } - else - { - LambdaHelper.InferTypesFromVarStmt(spf[spfnum].parameters[kk].type, en as SyntaxTree.function_lambda_definition, this); - exprs[kk] = convert_strong(en); - kk++; - } - } - } - // SSM 21.05.14 end - LambdaHelper.processingLambdaParametersForTypeInference--; - } - //lroman// - #endregion + // #2180 - в уникальной ситуации, когда из метода потомка вызывается метод расширения класса-предка добавлять Self (!) // Это значит, что dereferencing_value - это идентификатор и sil состоит только (?) из методов расширения