merged Sys.Web.Services 2.0 support in my branch:
[mono.git] / mcs / docs / new-anonymous-design.txt
1 Anonymous Methods and the TypeContainer resolve order
2 -----------------------------------------------------
3
4 Anonymous methods add another resolving pass to the TypeContainer framework.
5 The new code works like this:
6
7 * Everything which may contain anonymous methods or iterators now
8   implements the `IAnonymousHost' interface.  This applies to
9   `Method', `Constructor', `Accessor' and `Operator'.
10
11   We can already determine whether or not a method contains anonymous
12   methods or iterators at parsing time, but we can't determine their
13   types yet.  If we encounter an anonymous method or iterator while
14   parsing, we add the information to the current `IAnonymousHost'.
15
16   This means that at the end of the parsing stage, we already know
17   about all anonymous methods and iterators, but didn't resolve them
18   yet.
19
20 * After parsing, RootContext.ResolveTree() calls DefineType() on all
21   TypeContainers.
22
23 * Inside TypeContainer.DefineType(), we do the following:
24
25   - first we have to create our TypeBuilder via DefineTypeBuilder().
26
27   - after that, we scan all methods, constructors, operators and
28     property/indexer accessors for anonymous methods and iterators.
29
30     For each method which either contains anonymous methods or is
31     implemented as iterator, we create a new helper class (the "root
32     scope" of the anonymous method) and add it to the current type as
33     a nested class.
34
35     This is done by the new TypeContainer.ResolveMembers() method.
36
37   - when done, we call DefineNestedTypes() to descend into our nested
38     children.
39
40 * RootContext.PopulateTypes() calls TypeContainer.ResolveType() and
41   TypeContainer.DefineMembers() as usual and populates everything.
42
43 * In TypeContainer.EmitType(), we call DefineMembers() and EmitType()
44   on all our CompilerGeneratedClass'es once we're done emitting the
45   current type.
46
47 One of the hardest parts of the new anonymous methods implementation
48 was getting this resolve order right.  It may sound complicated, but
49 there are reasons why it's done this way.
50
51 Let's have a look at a small example:
52
53         =====
54         delegate void Foo ();
55
56         class X {
57                 public void Hello<U> (U u)
58
59                 public void Test<T> (T t)
60                 {
61                         T u = t;
62                         Hello (u);
63                         Foo foo = delegate {
64                                 Hello (u);
65                         };
66                         foo ();
67                 }
68         }
69         =====
70
71 After parsing this file, we already know that Test() contains an
72 anonymous method, but we don't know its type until resolving it.
73
74 Because Test() is a generic method, we need to create a generic helper
75 class and then transform all method type parameters into class type
76 parameters.
77
78 One key feature of the new code is using the normal TypeContainer
79 framework to create and use generic classes.  For each method
80 containing anonymous methods, we create one "root scope" which deals
81 with generics and also hosts any captured parameter and `this'.
82
83 In this example, this is done when calling DefineType() on `X's
84 TypeContainer, during the ResolveMembers() pass.  After that, we can
85 handle the helper classes just like normal nested classes, so
86 DefineNestedTypes() creates their TypeBuilders.
87
88 One important thing to keep in mind is that we neither know the type
89 of the anonymous methods nor any captured variables until resolving
90 `Test'.  Note that a method's block isn't resolved until
91 TypeContainer.EmitCode(), so we can't call DefineMembers() on our
92 CompilerGeneratedClass'es until we emitted all methods.
93
94
95 Anonymous Methods and Scopes:
96 -----------------------------
97
98 The new code fundamentally changes the concept of CaptureContexts and
99 ScopeInfos.  CaptureContext is completely gone while the ScopeInfo has
100 been completely redesigned.
101
102 Unfortunately, computing the "root scope" of an anonymous method is
103 very difficult and was the primary reason for the update in late
104 November 2006.  Consider the following example:
105
106         ====
107         TestDelegate d = null;
108         for (int i = 1; i <= 5; i++) {
109                 int k = i;
110                 TestDelegate temp = delegate {
111                         Console.WriteLine ("i = {0}, k = {1}", i, k);
112                         sum_i += 1 << i;
113                         sum_k += 1 << k;
114                 };
115                 temp ();
116                 d += temp;
117         }
118         ====
119
120 Note that we're instantiating the same anonymous method multiple times
121 inside a loop.  The important thing is that each instantiation must
122 get the current version of `k'; ie. we must create a new instance 'k's
123 helper-class for each instantiation.  They all share `i's helper-class.
124
125 This means that the anonymous method needs to be hosted in the inner
126 helper-class.
127
128 Because of that, we need to compute all the scopes before actually
129 creating the anonymous method.
130
131 Anonymous Methods and Generics:
132 -------------------------------
133
134 Creating and consuming generic types is very difficult and you have to
135 follow certain rules to do it right (the most important one is that
136 you may not use the class until it's fully created).
137
138 GMCS already has working code to do that - and one very important
139 policy in the new anonymous methods code is that it must not interfer
140 with GMCS's way of resolving and defining generic types; ie. everything
141 related to generics is handled during the normal TypeContainer
142 resolving process.
143
144 When the anonymous methods code kicks in, all the generic types are
145 already defined and ready for use.
146
147 Adding a new non-generic class to such a generic type is really easy
148 and not a problem - non-generic means that the new class does not
149 introduce any new type parameters; it may still use its containing
150 class'es type parameters:
151
152 Example:
153
154     class IAmGeneric<T>
155     {
156         class IAmNot // must derive from System.Object
157         {
158             // using the containing classe's type parameter is ok.
159             public T ButMayStillUseMyParentsT;
160         }
161     }
162
163
164 The new `Variable' abstraction:
165 -------------------------------
166
167 There is a new `Variable' abstraction which is used for locals and
168 parameters; all the knowledge about how to access a variable and
169 whether it's captured or not is now in that new abstract `Variable'
170 class.  The `LocalVariableReference' and `ParameterReference' now
171 share most of their code and have a common `VariableReference' base
172 class, which is also used by `This'.
173
174 `Variable' also controls whether or not we need to create a temporary
175 copy of a variable.
176
177 Before emitting any method, we scan over all its parameters and local
178 variables again and check whether any of them have been captured.
179
180 `Parameter' and `LocalInfo' both have a new ResolveVariable() method
181 which creates an instance of the new `Variable' class for each of
182 them.
183
184 If we're captured, a `Field' has already been created for the variable
185 and since we're called during the normal TypeContainer resolve / emit
186 process, there' no additional "magic" required; it "just works".
187
188     CAUTION: Inside the anonymous method, the `Variable's type
189              determines the variable's actual type - outside it
190              is the ParameterReference / LocalVariableReference's
191              type !
192
193     To make it more clear:
194
195         The type of a ParameterReference / LocalVariableReference
196         depends upon whether we're inside our outside the anonymous
197         method - and in case of generic, they are different !!!
198
199         The normal situation is that outside the anonymous method,
200         we may use the generic method parameters directly (ie.
201         MONO_TYPE_MVAR) - but inside the anonymous method, we're in
202         and generic class, not a generic method - so it's a generic
203         type parameter (MONO_TYPE_VAR).
204
205         There are several tests for this in my new test suite.
206
207     This does not only apply to variables; it's the same for types -
208     the same `T' may mean a completely different type depending upon
209     whether we're inside or outside the anonymous method: outside,
210     it's a generic method parameter (MONO_TYPE_MVAR) and inside, it's
211     a generic type parameter (MONO_TYPE_VAR) - so we already need to
212     handle this in the EmitContext to make SimpleNameResolve work.
213
214