In class/Microsoft.Build.Tasks:
[mcs.git] / docs / new-anonymous-design.txt
blob6342fb9aafc71451ddc2cdf27b62b49f769b2dc4
1 Anonymous Methods and the TypeContainer resolve order
2 -----------------------------------------------------
4 Anonymous methods add another resolving pass to the TypeContainer framework.
5 The new code works like this:
7 * Parsing
9   We can already determine whether or not a method contains anonymous
10   methods or iterators at parsing time, but we can't determine their
11   types yet.
13   This means that at the end of the parsing stage, we already know
14   about all anonymous methods and iterators, but didn't resolve them
15   yet.
17 * DefineType
19   Anonymous method containers are not created until they are needed
20   which means we cannot use standard DefineType to setup type
21   container.
22   
23   Note: Even if block looks like anonymous method, it does not necessary
24   mean it's anonymous method, expression trees are good example.
26 * EmitType
28   At this point we enter anonymous methods land. We call Resolve on
29   method block which when hits anonymous method expression we start
30   anonymous method definition.
32 One of the hardest parts of the new anonymous methods implementation
33 was getting this resolve order right.  It may sound complicated, but
34 there are reasons why it's done this way.
36 Let's have a look at a small example:
38         =====
39         delegate void Foo ();
41         class X {
42                 public void Hello<U> (U u)
44                 public void Test<T> (T t)
45                 {
46                         T u = t;
47                         Hello (u);
48                         Foo foo = delegate {
49                                 Hello (u);
50                         };
51                         foo ();
52                 }
53         }
54         =====
56 After parsing this file, we already know that Test() contains an
57 anonymous method, but we don't know whether it needs to capture local
58 variable or access this pointer.
60 Because Test() is a generic method, it complicates things further
61 as we may need to create generic type container and transform all method
62 type parameters into class type parameters to keep method signature
63 compatible with requested delegate signature.
65 One key feature of the new code is using minimal possible anonymous
66 method overhead. Based on whether an anonymous method needs access to
67 this pointer or has access to local variables from outher scope new
68 TypeContainer (anonymous method storey) is created. We may end up with
69 up to 3 scenarios.
71 1. No access to local variable or this
73         Anonymous method is emitted in a compiler generated static method
74 inside same class as current block.
76 2. No access to local variable but this is accessible
78         Anonymous method is emitted in a compiler generated instance method
79 inside same class as current block.
81 3. Local variable is accessible
83         New nested class (anonymous method storey) is created and anonymous
84 method block is emitted as an instance method inside this nested class.
86 Note: The important detail for cases 1 and 2 is that both methods are
87 created inside current block class, which means they can end up inside
88 anonymous method storey when parent scope needs access to local variable.
90 One important thing to keep in mind is that we neither know the type
91 of the anonymous methods nor any captured variables until resolving
92 `Test'.  Note that a method's block isn't resolved until
93 TypeContainer.EmitCode(), so we can't call DefineMembers() on our
94 CompilerGeneratedClass'es until we emitted all methods.
96 Anonymous Methods and Scopes:
97 -----------------------------
99 The new code fundamentally changes the concept of CaptureContexts and
100 ScopeInfos. CaptureContext is completely gone together with ScopeInfo.
102 Unfortunately, computing the optimal "root scope" of an anonymous
103 method is very difficult and was the primary reason for the update in
104 late November 2006.  Consider the following example:
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         ====
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.
125 This means that the anonymous method needs to be hosted in the inner
126 helper-class.
128 Because of that, we need to compute all the scopes before actually
129 creating the anonymous method.
131 Anonymous Methods and Generics:
132 -------------------------------
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).
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.
144 However, there is a problem when we are dealing with generics variables.
145 They may end up to be captured but their local generic type has been
146 already resolved. To handle this scenario type mutation was introduced,
147 to convert any method type parameter (MVAR) to generic type parameter
148 (VAR). This process is not straighforward due to way how S.R.E deals
149 with generics and we have to recontruct each reference of mutated
150 (MVAR->VAR) generic type.
153 The new `Variable' abstraction:
154 -------------------------------
156 There is a new `Variable' abstraction which is used for locals and
157 parameters; all the knowledge about how to access a variable and
158 whether it's captured or not is now in that new abstract `Variable'
159 class.  The `LocalVariableReference' and `ParameterReference' now
160 share most of their code and have a common `VariableReference' base
161 class, which is also used by `This'.
163 `Variable' also controls whether or not we need to create a temporary
164 copy of a variable.
166 `Parameter' and `LocalInfo' both have a new ResolveVariable() method
167 which creates an instance of the new `Variable' class for each of
168 them.
170 If we're captured, a `Field' has already been created for the variable
171 and since we're called during the normal TypeContainer resolve / emit
172 process, there' no additional "magic" required; it "just works".
174     CAUTION: Inside the anonymous method, the `Variable's type
175              determines the variable's actual type - outside it
176              is the ParameterReference / LocalVariableReference's
177              type !
179     To make it more clear:
181         The type of a ParameterReference / LocalVariableReference
182         depends upon whether we're inside our outside the anonymous
183         method - and in case of generic, they are different !!!
185         The normal situation is that outside the anonymous method,
186         we may use the generic method parameters directly (ie.
187         MONO_TYPE_MVAR) - but inside the anonymous method, we're in
188         and generic class, not a generic method - so it's a generic
189         type parameter (MONO_TYPE_VAR).
191         There are several tests for this in my new test suite.
193     This does not only apply to variables; it's the same for types -
194     the same `T' may mean a completely different type depending upon
195     whether we're inside or outside the anonymous method: outside,
196     it's a generic method parameter (MONO_TYPE_MVAR) and inside, it's
197     a generic type parameter (MONO_TYPE_VAR) - so we already need to
198     handle this in the EmitContext to make SimpleNameResolve work.