- About Scala
- Documentation
- Code Examples
- Software
- Scala Developers
Is asSeenFrom what I need?
Wed, 2011-08-17, 17:01
This is a followup to my earlier question about getting hold of the A#B#C representation of a nested type (instead of B.this.C). As suggested, using the type-erased version of the type works fine. But it's given me another problem. I suspect that asSeenFrom might be the solution to my problem, but I'm not sure?
The problem:
I'm working on a compiler plugin that generates source code (because of the well understood problems with generating synthetic methods in compiler plugins).
Given input code like this:
> class A {
> class B
> def m: B = ...
> }
I'm generating code like this:
> trait Mock$A {
> trait Mock$B
> def m: A#B = ...
> }
And that works fine if I use the erasure of the return type of method m to generate the code.
But imagine that I have code like this:
> class A {
> def m: Iterator[A] = ...
> }
Now I want to generate code like this:
> trait Mock$A {
> def m: Iterator[A] = ...
> }
If I use the erasure of the return type of method m, my code won't compile (because it complains that Iterator must take a type parameter).
I could special case this and use the erasure for nested types and not for other types, but this feels messy to me (and won't work if the nested type is parameterised).
Spelunking the code, asSeenFrom seems like it might give me what I want? But I've been unable to find a way to persuade it to generate what I'm after. I suspect that this is because I'm misunderstanding it - is there any documentation or sample code that I could spelunk to understand it better? Or should I be looking at a different approach?
--
paul.butcher->msgCount++
Snetterton, Castle Combe, Cadwell Park...
Who says I have a one track mind?
http://www.paulbutcher.com/
LinkedIn: http://www.linkedin.com/in/paulbutcher
MSN: paul@paulbutcher.com
AIM: paulrabutcher
Skype: paulrabutcher
Thu, 2011-08-18, 12:07
#2
Re: Is asSeenFrom what I need?
On 18 Aug 2011, at 08:14, Adriaan Moors wrote:
> here, we're talking "absolute types": e.g., the typeref to the symbol for class A must become the typeref to the symbol for the class that mocks it
Sorry - I fear that my ill-chosen example may have confused matters. I'm not asking how to turn a reference to A into a reference to Mock$A (I already have a good solution to that using generated implicits).
I'm asking about how to generate references to types within the generated Mock$A. Imagine that my class A looks like this:
> class A {
> class B
> def m1: B = ...
> def m2: Iterator[A] = ...
> }
My plugin is generating the source code for Mock$A (Mock$A doesn't exist as a tree or anything else within the plugin, because of the well-known problems with generating synthetic methods in plugins - instead the plugin is writing a file containing the source code for Mock$A which is subsequently compiled in a separate invocation of the compiler).
So I've got a method within my plugin which is generating the source code for methods within Mock$A. What it needs to generate is:
> trait Mock$A {
> trait Mock$B
> def m1: A#B = ...
> def m2: Iterator[A] = ...
> }
The problem is how to write the code that generates the return type of methods within Mock$A. Here's (a slightly simplified version of) how I currently do it (where mockMethod is called once for each method of the class for which I'm generating a mock):
> def mockMethod(method: Symbol): String =
> method.info match {
> case MethodType(params, result) => mockMethod(method, Some(params), result)
> case NullaryMethodType(result) => mockMethod(method, None, result)
> }
>
> def mockMethod(method: Symbol, params: Option[List[Symbol]], result: Type) =
> methodDeclarationWithReturnType(method, params, result) +" = "+ mockBody(method, params)
>
> def methodDeclarationWithReturnType(method: Symbol, params: Option[List[Symbol]], result: Type) =
> methodDeclaration(method, params) +": "+ result
That works fine as long as there are no nested types, but for the example above, it generates:
> trait Mock$A {
> trait Mock$B
> def m1: A.this.B = ...
> def m2: Iterator[A] = ...
> }
I can make it work for nested types by changing methodDeclarationWithReturnType to:
> def methodDeclarationWithReturnType(method: Symbol, params: Option[List[Symbol]], result: Type) =
> methodDeclaration(method, params) +": "+ erasure.erasure(result)
But this now generates:
> trait Mock$A {
> trait Mock$B
> def m1: A#B = ...
> def m2: Iterator = ...
> }
:-(
I don't think that I can use substSym to address this, can I? Or have I misunderstood something?
Thanks for your help - it's much appreciated!
--
paul.butcher->msgCount++
Snetterton, Castle Combe, Cadwell Park...
Who says I have a one track mind?
http://www.paulbutcher.com/
LinkedIn: http://www.linkedin.com/in/paulbutcher
MSN: paul@paulbutcher.com
AIM: paulrabutcher
Skype: paulrabutcher
On Wed, Aug 17, 2011 at 6:00 PM, Paul Butcher <paul@paulbutcher.com> wrote:
that looks like a symbol substitution to me (see TreeSymSubstituter for trees, and tp.substSym(from, to) for types)
asSeenFrom is about interpreting "relative types" (such as type parameters and `this`) in varying contexts (which affect the meaning of those types)
here, we're talking "absolute types": e.g., the typeref to the symbol for class A must become the typeref to the symbol for the class that mocks it(well, there's some dependency in the sense that you'll also need to rewrite prefixes of typerefs in the same way, not just the symbol, since I assume the nesting of classes changes as well, but the rewrite is not context-sensitive)
adriaan