Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

introduces Unquote #112

Merged
merged 17 commits into from Mar 1, 2015
Merged

introduces Unquote #112

merged 17 commits into from Mar 1, 2015

Conversation

xeno-by
Copy link
Member

@xeno-by xeno-by commented Mar 1, 2015

No description provided.

I'm about to significantly change @ast codegen, so we need these additional tests
to make sure that nothing breaks down the road.
I'm about to introduce impl.Unquote, which has to be a subtype of all tree nodes.
However unpleasant the consequences are, we have to deal with them, because
we currently don't know of an alternative technology to implement quasiquotes
in a principled manner.

What we discussed with Denys is that we might be able to get back the
guarantees offered by sealedness if we find a way to customize exhaustivity
checks in the pattern matcher from a compiler plugin. People are going to
use scalahost anyway, so it could also do additional domain-specific
exhaustivity checking.

This commit also comes with some refactoring of the adt/ast infrastructure.
In particular, all checks now return q"()" instead of q"", which has uncovered
a terrible bug. If a trait def has the INTERFACE flag set, then essentially
any statement in its body (e.g. q"()") is going to crash the compiler.
Therefore, I'm manually resetting the INTERFACE flag in @root and @Branch.
This, in turn, led to problems with `@branch trait Defn`, because it makes
scalac emit `Defn$class`, which comes in conflict with `Defn$Class`.
To address that, I had to temporarily disable all checks for Defn.
As explained in the parent's commit message, we had to reset INTERFACE,
because our codegen emits statements into templates of the annottees,
and GenICode really doesn't like it when a template of an INTERFACE trait
has statements in it.

Now it's time for a principled solution. All statements are now moved
to companions of the annottees, and everyone's happy.
I'm about to introduce scala.meta.internal.ast.Unquote, which has to be
a subtype of all tree nodes. For that, we need to make it possible to
subclass @ast classes.

The solution that I'll be pursuing in this and subsequent commits
is splitting `@ast class Foo` into `trait Foo; object Foo { class Impl extends Foo }`.
This is a very heavy-handed way of organizing the @ast codegen, but it looks
like we don't have much choice.

A pleasant side-effect of this change is the ability to proxy trees,
so that, for example, we can lazily load bodies of vals/vars/defs.
Nothing in that direction is implemented just yet. We will cross that bridge
when we get there - now is not the time.
In order to be able to subclass all @ast traits, Unquote needs those
traits to be devoid of any methods.

Therefore, since this commit, we split all @ast classes into three entities:
an empty trait, an API-carrying trait and an implementation class.
Unfortunately, having an Interface => Api implicit conversion is not enough,
because we have things like Member.name in the semantic API, and those
come in conflict.

Therefore, we'll have to move ast fields to the interface part, and then
have Unquote implement ALL those fields with ??? or something similar.
This is a funny class that is a subclass of all ast classes in scala.meta.
At the moment it's just `@bottom @ast class Unquote() extends Tree`, but
later on we'll decide how it fields should look like.
Now when we accumulate all ast classes in parents of Unquote,
we don't need an additional mechanism to track those classes.
It turns out that it can be perfectly represented by meta.Name.Indeterminate.
This takes care of the last place in the tree API, where we represented
names with strings, and makes our tree API maximally friendly to Unquote.

It's a bit of stretch to use Name.Qualifier here, because along with
anonymous and indeterminate names (which fit), Name.Qualifier also covers
Term.This (which doesn't fit), but I didn't feel like introducing a new
public trait just to have this extra bit of static safety. Maybe in the future
we'll have to change this decision.

Having this change in place also means that This and Super don't have to be
names anymore. This, in turn, means that Name.Qualifier can no longer be
a Name and becomes a Ref. This also means that Member.name can no longer
be a Name, but that was too much for me, so now it's still a Name, but it
does not return This anymore. All in all, this worked pretty well.
I'm not sure whether I like the fact that `this` and `super` are no longer names,
but I can try to get accustomed to that.
xeno-by added a commit that referenced this pull request Mar 1, 2015
@xeno-by xeno-by merged commit 49258a7 into scalameta:master Mar 1, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

1 participant