This commit is contained in:
Ivan Bondarev 2021-01-06 14:20:07 +01:00
parent 5f647d029c
commit 4db76663d1
4 changed files with 214 additions and 193 deletions

13
TestSuite/lambdas15.pas Normal file
View file

@ -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.

View file

@ -1,8 +1,8 @@
uses System, System.Collections.Generic;
uses System, System.Collections.Generic;
procedure p<X, Y> (a: X; b: Action<IEnumerable<Dictionary<X, Y>>>; 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

View file

@ -1,8 +1,8 @@
uses System, System.Collections.Generic;
uses System, System.Collections.Generic;
procedure p<X, Y> (b: Func<IEnumerable<Y>, 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

View file

@ -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<function_node> 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<T,T1,...TN> - тип возвращаемого значения
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<function_node> 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<T,T1,...TN> - тип возвращаемого значения
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 состоит только (?) из методов расширения