Skip to content

Commit 11252e8

Browse files
committed
Better handling of copied captured lambda vars
1 parent e1cd82c commit 11252e8

File tree

4 files changed

+86
-4
lines changed

4 files changed

+86
-4
lines changed

src/vm/symbol/func.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -694,6 +694,7 @@ VariableSymbol AddUpValue(VariableSymbol src)
694694
//NOTE: if there's no capture info we consider a default value
695695
UpvalMode mode = UpvalMode.STRONG;
696696
captures.TryGetValue(src, out mode);
697+
local._upvalue_mode = mode;
697698

698699
var upval = new AST_UpVal(local, src, mode,
699700
//TODO: should be the line of its usage

src/vm/symbol/variable.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,13 @@ public int scope_idx
2121
//referenced upvalue, keep in mind it can be a 'local' variable from the
2222
//outer lambda wrapping the current one
2323
internal VariableSymbol _upvalue;
24+
internal UpvalMode _upvalue_mode;
2425
internal bool _is_ref =>
2526
//let's make sure 'this' symbol is not a ref (since it doesn't make sense)
2627
!(_scope_idx == 0 && name == "this") &&
2728
(_is_ref_decl ||
2829
(this is FuncArgSymbol fs && fs.is_ref) ||
29-
(_upvalue?._is_ref ?? false))
30+
((_upvalue?._is_ref ?? false) && _upvalue_mode == UpvalMode.STRONG))
3031
;
3132
#endif
3233

src/vm/vm.exec.cs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2523,8 +2523,20 @@ unsafe static void OpcodeSetUpval(VM vm, ExecState exec, ref Region region, ref
25232523
upval.frame_local_idx = func_ptr_local_idx;
25242524

25252525
ref var val = ref frame.locals.vals[frame.locals_offset + frame_local_idx];
2526-
val._refc?.Retain();
2527-
upval.val = val;
2526+
2527+
if(val._refc != null)
2528+
{
2529+
//TODO: it's quite a rare case so we use reflection here
2530+
if(mode == UpvalMode.COPY && val._refc is ValRef vr)
2531+
upval.val = vr.val.Clone();
2532+
else
2533+
{
2534+
val._refc.Retain();
2535+
upval.val = val;
2536+
}
2537+
}
2538+
else
2539+
upval.val = val;
25282540
}
25292541
#if BHL_USE_OPCODE_SWITCH
25302542
break;

tests/test_lambda.cs

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1398,7 +1398,7 @@ func test()
13981398
}
13991399

14001400
[Fact]
1401-
public void TestCaptureMixCopyAndStrong()
1401+
public void TestCaptureMixCopyAndReference()
14021402
{
14031403
string bhl = @"
14041404
func test()
@@ -1423,6 +1423,74 @@ func test()
14231423
CommonChecks(vm);
14241424
}
14251425

1426+
[Fact]
1427+
public void TestCapturedScalarReferencePassedAsCopy()
1428+
{
1429+
string bhl = @"
1430+
func test()
1431+
{
1432+
int v = 10
1433+
1434+
start(coro func() [v] {
1435+
yield()
1436+
yield()
1437+
yield()
1438+
trace("";1:"" + (string)v)
1439+
})
1440+
v = 100
1441+
start(coro func() {
1442+
yield()
1443+
yield()
1444+
trace("";2:"" + (string)v)
1445+
})
1446+
}
1447+
";
1448+
1449+
var log = new StringBuilder();
1450+
var ts_fn = new Action<Types>((ts) => { BindTrace(ts, log); });
1451+
1452+
var vm = MakeVM(bhl, ts_fn);
1453+
Execute(vm, "test");
1454+
Assert.Equal(";2:100;1:10", log.ToString());
1455+
CommonChecks(vm);
1456+
}
1457+
1458+
[Fact]
1459+
public void TestCapturedObjReferencePassedAsCopyButNotACopy()
1460+
{
1461+
string bhl = @"
1462+
class Foo {
1463+
int f
1464+
}
1465+
1466+
func test()
1467+
{
1468+
Foo foo = {f: 42}
1469+
1470+
start(coro func() [foo] {
1471+
yield()
1472+
yield()
1473+
yield()
1474+
trace("";1:"" + (string)foo.f)
1475+
})
1476+
foo.f = 100
1477+
start(coro func() {
1478+
yield()
1479+
yield()
1480+
trace("";2:"" + (string)foo.f)
1481+
})
1482+
}
1483+
";
1484+
1485+
var log = new StringBuilder();
1486+
var ts_fn = new Action<Types>((ts) => { BindTrace(ts, log); });
1487+
1488+
var vm = MakeVM(bhl, ts_fn);
1489+
Execute(vm, "test");
1490+
Assert.Equal(";2:100;1:100", log.ToString());
1491+
CommonChecks(vm);
1492+
}
1493+
14261494
[Fact]
14271495
public void TestClosure()
14281496
{

0 commit comments

Comments
 (0)