Lowering для foreach

Материал из Вики проекта PascalABC.NET
Перейти к навигацииПерейти к поиску

Такой код

foreach var x in l do
  Print(x);

заменяется на

var a: IEnumerable<yield_unknown_foreach_type> := l;
  
var en := a.GetEnumerator();
while en.MoveNext do
begin
  var curr := en.Current;
  Print(curr);
end;

на синтаксическом уровне.

После этого генератор yield преобразует синтаксическое дерево так, что строка:

var a: IEnumerable<yield_unknown_foreach_type> := l;

преобразуется в

// поле специального класса
a: IEnumerable<yield_unknown_foreach_type>
...
// оператор в методе MoveNext этого класса
a := l;

Основная идея

Основная идея автоопределния типа в yield - ввести семантический тип auto_type, который перевычисляется при первом присваивании переменной этого типа

Для IEnumerable<yield_unknown_foreach_type> она трансформируется следующим образом: yield_unknown_foreach_type на семантике преобразуется в ienumerable_auto_type. При обработке присваивания на семантике если тип в левой части - IEnumerable<ienumerable_auto_type>, то он преобразуется к типу правой части в момент первого присваивания (это произойдёт в теле метода MoveNext())

Реализация в syntax_tree_visitor.cs

Так вот, yield_unknown_foreach_type при преобразовании синтаксиса в семантику переводится в специальный семантический тип auto_type, который исправляется в момент первого присваивания переменной a. Таким образом, в syntax_tree_visitor.cs в

public override void visit(SyntaxTree.assign _assign)

будет код, аналогичный автоопределению типа в момент первого присваивания для переменных типа auto_type:

public override void visit(SyntaxTree.assign _assign)
{
  ...
if (to.type is compiled_generic_instance_type_node && (to.type as compiled_generic_instance_type_node).instance_params[0] is ienumerable_auto_type)
{
    var tt = to.type;
    type_node elem_type = null;
    var b = FindIEnumerableElementType(from.type, ref elem_type);
    if (!b)
        AddError(null, "FindIEnumerableElementType returns false");
    if (to is class_field_reference)
    {
        var cfr = to as class_field_reference;

        var IEnumType = new template_type_reference(new named_type_reference("System.Collections.Generic.IEnumerable"), new template_param_list(new semantic_type_node(elem_type)));

        cfr.field.type = convert_strong(IEnumType);
        cfr.type = cfr.field.type;
    }
    else if (to is local_block_variable_reference)
    {
        var lvr = to as local_block_variable_reference;

        var IEnumType = new template_type_reference(new named_type_reference("System.Collections.Generic.IEnumerable"), new template_param_list(new semantic_type_node(elem_type)));

        lvr.var.type = convert_strong(IEnumType); // замена типа у описания переменной
        lvr.type = lvr.var.type;                  // замена типа у переменной
    }
}

Дополнительная правка в связи с глобальными таблицами genericов

Увы, в результате реализации этого кода происходила ошибка на этапе генерации IL-кода. Выяснилось, что тип