Подтвердить что ты не робот

Scala macros: В чем разница между типизированными (aka typechecked) нетипизированными деревьями

Я начинаю с макросов scala, они потрясающие, но я столкнулся с различием между типизированным (aka typechecked) и нетипизированным Tree s.

Например, вы не можете вызвать c.eval с помощью дерева с меткой typechecked по какой-либо причине. Я не могу найти документацию по этому "typechecked" в документации по макросам scala (я знаю, что они все еще работают над этим, это может быть что-то, что нужно добавить однажды).

Что означает, что Tree должен быть проверен typechecked? Почему они настолько отличаются друг от друга, что, очевидно, c.eval не может иметь дело с typechecked Tree (обратный мне будет больше смысла).

Я думаю, это, наверное, компилятор 101, но я не принимал этот курс:( Любое объяснение или указатель на статьи/документы будут оценены!

4b9b3361

Ответ 1

Теоретическая часть

Это архитектурная особенность скаляса, которая начала проникать в открытый API, когда мы раскрыли внутренние структуры данных компилятора во время компиляции/времени выполнения во 2.10.

Очень грубо говоря, front-интерфейс scalac состоит из синтаксического анализатора и typer, оба из которых работают с деревьями и производят деревья в качестве результата. Однако свойства этих деревьев совершенно разные, и это происходит из-за того, что деревья, созданные парсером, не имеют атрибутов (их поле symbol установлено на NoSymbol и их поле tpe установлено на null), тогда как деревья, созданные typer приписываются.

Теперь вы можете задаться вопросом, какая разница это может сделать, потому что это просто symbol и tpe, правильно? Однако в scalac это больше, чем просто. Чтобы выполнить свою работу, typer изменяет структуру обработанных АСТ, уничтожая некоторые оригинальные деревья и производя синтетические деревья. К сожалению, иногда эти преобразования необратимы, а это означает, что если одно typechecks дерево, а затем стирает все назначенные атрибуты, результирующее дерево больше не будет иметь смысла (https://issues.scala-lang.org/browse/SI-5464).

Хорошо, но зачем нужно стирать (или в атрибутах scalac, reset, как в resetLocalAttrs или resetAllAttrs) атрибутах typechecked деревьев? Ну, эта необходимость проистекает из другой детали реализации - символов и их цепочек владельцев. Всего несколько дней назад я написал некоторые подробности об этом на scala -internals: https://groups.google.com/d/msg/scala-internals/rIyJ4yHdPDU/qS-7DreEbCwJ, но в двух словах вы можете 't typecheck дерево в каком-то лексическом контексте, а затем просто используйте его в другом лексическом контексте (что необходимо для c.eval).

Итак, подведем итог к управлению уровнем дерева:

  • Ненулированные деревья (также называемые деревьями парсеров или деревья без атрибутов) наблюдаются в отличие от типизированных деревьев (также называемых деревьями с тирерами, деревьями с проверкой или деревьями)
  • Существуют два основных различия между этими двумя вкусами дерева: a) типизированные деревья имеют символы и типы, заданные с помощью typechecker, b) типизированные деревья имеют несколько разные формы.
  • Обычно, если какой-либо API-интерфейс компилятора принимает дерево, тогда будут выполняться как нетипизированные, так и типизированные деревья. Однако в некоторых случаях (один из которых я изложил выше) подходят только нетипизированные или только типизированные деревья.
  • Можно перейти от нетипизированного дерева к типизированному дереву, вызвав Context.typecheck (отражение во время компиляции) или ToolBox.typecheck (отражение во время выполнения), но вернувшись от типизированного дерева к нетипизированному дереву через resetLocalAttrs или resetAllAttrs в настоящее время является ненадежным из-за https://issues.scala-lang.org/browse/SI-5464.

Итак, как вы можете видеть, наши деревья довольно капризны, что приносит большую сложность в метапрограммирование в Scala.

Однако хорошая новость заключается в том, что эта сложность не продиктована некоторыми фундаментальными вескими причинами, которые возникают в компиляторе 101. Вся сложность является случайной, и мы планируем выселить ее шаг за шагом, пока она не исчезнет. https://groups.google.com/forum/#!topic/scala-internals/TtCTPlj_qcQ (также опубликовано пару дней назад) - это первый шаг в этом направлении. Оставайтесь с нами для других лакомств, которые также могут прибыть в этом году!

Практическая часть

После тщательного пугания вы, подробно изложив все детали и намекая на таинственные случаи, когда ничего не работает, я хотел бы отметить, что обычно обычно не нужно знать об этих вещах при использовании макросов. Очень часто работают как нетипизированные деревья (вручную построенные АСТ, квазиквадраты), так и типизированные деревья (макро-аргументы).

В случае, когда scalac хочет получить конкретный оттенок дерева, он либо расскажет вам, как c.eval, либо иногда сбой в вашем лице (сбои RefChecks, LambdaLift и GenICode - это огромные индикаторы, которые деревья смешивались во время макрорасширения - в тех случаи используют resetLocalAttrs, как описано в https://groups.google.com/forum/#!msg/scala-internals/rIyJ4yHdPDU/qS-7DreEbCwJ). Фиксирование это мой главный приоритет, и я сейчас работаю над этим. Может случиться так, что исправления превратятся в 2.11.0, и этот ответ скоро устареет:)