Skip to content

Commit 34e915b

Browse files
authored
Merge pull request #446 from mortend/lambda-generic
compiler: support lamdas in generic classes
2 parents 4a5d527 + 9c05216 commit 34e915b

File tree

3 files changed

+106
-25
lines changed

3 files changed

+106
-25
lines changed

src/compiler/core/IL/Building/Functions/Lambdas/ClosureConvertFunction.cs

+80-24
Original file line numberDiff line numberDiff line change
@@ -60,30 +60,41 @@ Closure CreateClosure(Closure parent, HashSet<Variable> locals)
6060
if (parent != null && locals.Count == 0)
6161
return parent;
6262

63-
var type = new ClassType(
63+
DataType masterType = new ClassType(
6464
Source.Unknown,
6565
Function.DeclaringType,
6666
Function.Name + " lambda closure",
6767
Modifiers.Private | Modifiers.Generated,
6868
Function.DeclaringType.GetUniqueIdentifier(Function.Name + "_$closure"));
6969

70-
type.SetBase(Essentials.Object);
70+
masterType.SetBase(Essentials.Object);
7171

7272
var constrBody = new Scope(
7373
Source.Unknown,
7474
new CallConstructor(Source.Unknown, Essentials.Object.TryGetConstructor()));
7575

76-
type.Constructors.Add(
76+
masterType.Constructors.Add(
7777
new Constructor(
7878
Source.Unknown,
79-
type,
79+
masterType,
8080
"",
8181
Modifiers.Public | Modifiers.Generated,
8282
ParameterList.Empty,
8383
constrBody));
8484

85-
Function.DeclaringType.NestedTypes.Add(type);
86-
_generatedTypes.Add(type);
85+
Function.DeclaringType.NestedTypes.Add(masterType);
86+
_generatedTypes.Add(masterType);
87+
88+
var type = TypeBuilder.Parameterize(masterType);
89+
var isGenericType = !type.IsMasterDefinition;
90+
91+
if (isGenericType)
92+
{
93+
var ctor = masterType.Constructors[0];
94+
var parameterizedCtor = new Constructor(ctor.Source, type, ctor.DocComment, ctor.Modifiers, ctor.Parameters, ctor.Body);
95+
parameterizedCtor.SetMasterDefinition(ctor);
96+
type.Constructors.Add(parameterizedCtor);
97+
}
8798

8899
var decl = new VariableDeclaration(
89100
Source.Unknown,
@@ -103,44 +114,59 @@ Closure CreateClosure(Closure parent, HashSet<Variable> locals)
103114
{
104115
if (_closureVars.This)
105116
{
117+
var thisType = TypeBuilder.Parameterize(Function.DeclaringType);
106118
var field = new Field(
107119
Source.Unknown,
108-
type,
109-
type.GetUniqueIdentifier("self"),
120+
masterType,
121+
masterType.GetUniqueIdentifier("self"),
110122
"",
111123
Modifiers.Public | Modifiers.Generated,
112124
0,
113-
Function.DeclaringType);
125+
thisType);
114126

115-
type.Fields.Add(field);
116-
117-
statements.Add(result.StoreThis(new This(Function.DeclaringType)));
127+
masterType.Fields.Add(field);
128+
statements.Add(result.StoreThis(new This(thisType)));
118129
}
119130

120131
foreach (var p in _closureVars.Params)
121132
{
122133
var field = new Field(
123134
p.Source,
124-
type,
125-
type.GetUniqueIdentifier(p.Name),
135+
masterType,
136+
masterType.GetUniqueIdentifier(p.Name),
126137
"",
127138
Modifiers.Public | Modifiers.Generated,
128139
0,
129140
p.Type);
130141

131-
type.Fields.Add(field);
132-
result.ParameterFields[p] = field;
142+
var parameterizedField = field;
143+
144+
if (isGenericType)
145+
{
146+
parameterizedField = new Field(
147+
field.Source, type,
148+
field.Name,
149+
field.DocComment,
150+
field.Modifiers,
151+
field.FieldModifiers,
152+
field.ReturnType);
153+
parameterizedField.SetMasterDefinition(field);
154+
type.Fields.Add(parameterizedField);
155+
}
156+
157+
masterType.Fields.Add(field);
158+
result.ParameterFields[p] = parameterizedField;
133159
statements.Add(result.Store(p, new LoadArgument(Source.Unknown, Function, ParamIndex(p))));
134160
}
135161
}
136162
// Non-root closures get a parent field for the parent closure
137163
else
138164
{
139-
type.Fields.Add(
165+
masterType.Fields.Add(
140166
new Field(
141167
Source.Unknown,
142-
type,
143-
type.GetUniqueIdentifier("parent"),
168+
masterType,
169+
masterType.GetUniqueIdentifier("parent"),
144170
"",
145171
Modifiers.Public | Modifiers.Generated,
146172
0,
@@ -152,15 +178,30 @@ Closure CreateClosure(Closure parent, HashSet<Variable> locals)
152178
{
153179
var field = new Field(
154180
v.Source,
155-
type,
156-
type.GetUniqueIdentifier(v.Name),
181+
masterType,
182+
masterType.GetUniqueIdentifier(v.Name),
157183
"",
158184
Modifiers.Public | Modifiers.Generated,
159185
0,
160186
v.ValueType);
161187

162-
type.Fields.Add(field);
163-
result.VariableFields[v] = field;
188+
var parameterizedField = field;
189+
190+
if (isGenericType)
191+
{
192+
parameterizedField = new Field(
193+
field.Source, type,
194+
field.Name,
195+
field.DocComment,
196+
field.Modifiers,
197+
field.FieldModifiers,
198+
field.ReturnType);
199+
parameterizedField.SetMasterDefinition(field);
200+
type.Fields.Add(parameterizedField);
201+
}
202+
203+
masterType.Fields.Add(field);
204+
result.VariableFields[v] = parameterizedField;
164205
}
165206

166207
return result;
@@ -247,7 +288,7 @@ public override void Begin(ref Expression e, ExpressionUsage u)
247288

248289
_lambdaMethod = new Method(
249290
e.Source,
250-
_closureStack.Peek().Type,
291+
closureType,
251292
"Lambda method",
252293
Modifiers.Public | Modifiers.Generated,
253294
closureType.GetUniqueIdentifier("generated_lambda"),
@@ -257,6 +298,21 @@ public override void Begin(ref Expression e, ExpressionUsage u)
257298

258299
closureType.Methods.Add(_lambdaMethod);
259300

301+
if (!closureType.IsMasterDefinition)
302+
{
303+
var masterMethod = new Method(
304+
_lambdaMethod.Source,
305+
closureType.MasterDefinition,
306+
_lambdaMethod.DocComment,
307+
_lambdaMethod.Modifiers,
308+
_lambdaMethod.Name,
309+
_lambdaMethod.ReturnType,
310+
_lambdaMethod.Parameters,
311+
_lambdaMethod.Body);
312+
_lambdaMethod.SetMasterDefinition(masterMethod);
313+
closureType.MasterDefinition.Methods.Add(masterMethod);
314+
}
315+
260316
break;
261317
}
262318
}

src/compiler/core/Syntax/Builders/TypeBuilder.Parameterize.cs

+4-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@ public DataType Parameterize(DataType dt)
2525
if (it.MasterDefinition == dt.MasterDefinition)
2626
return Parameterize(it);
2727

28-
throw new FatalException(dt.Source, ErrorCode.I3044, "Unable to find parameterized version of " + dt.Quote());
28+
var pr = CreateParameterizableInnerType(dt, pp);
29+
pr.SetMasterDefinition(dt.MasterDefinition);
30+
pp.NestedTypes.Add(pr);
31+
return pr;
2932
}
3033

3134
var pt = Parameterize(dt.Source, dt, dt.GenericParameters);

tests/src/UnoTest/General/Lambdas.uno

+22
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,28 @@ namespace UnoTest.General
445445

446446
Assert.AreEqual("Yo!", f().Message);
447447
}
448+
449+
[Test]
450+
public void GenericContext()
451+
{
452+
var test = new GenericContextHelper<int>(10);
453+
Assert.AreEqual(10, test.Run());
454+
}
455+
456+
class GenericContextHelper<T>
457+
{
458+
Func<T> f;
459+
460+
public GenericContextHelper(T arg)
461+
{
462+
f = () => arg;
463+
}
464+
465+
public T Run()
466+
{
467+
return f();
468+
}
469+
}
448470
}
449471

450472
public class LambdasClashClass

0 commit comments

Comments
 (0)