Skip to content

Commit 986e867

Browse files
committed
Preserve tuple elements and unboxed dynamic indices
git-bug: ac72b510da295835d7420e58584ceafbf3b49b8b88ca64d204ba81ed45a6a3d2
1 parent 3c43e50 commit 986e867

5 files changed

Lines changed: 314 additions & 14 deletions

File tree

‎crates/zyntax_builtins/src/dynamic.rs‎

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1456,6 +1456,83 @@ pub(crate) fn declarations(policy: &Policy, list_type: TypeId) -> Vec<Decl> {
14561456
)),
14571457
],
14581458
));
1459+
// Positional indexing already has an unboxed integer in the Python
1460+
// frontend (notably while unpacking nested tuples). Keep that integer
1461+
// through the dispatch instead of allocating a box only to read it
1462+
// back in every list branch. Dicts still need a boxed key.
1463+
let position = local("position", i64());
1464+
d.push(define(
1465+
"zb_any_getitem_i64",
1466+
&[&x, &position],
1467+
any(),
1468+
vec![
1469+
cat.decl(category(x.e())),
1470+
when(
1471+
is(&cat, STR),
1472+
vec![ret(box_str(call(
1473+
"zb_str_get",
1474+
vec![get_str(x.e()), position.e()],
1475+
string(),
1476+
)))],
1477+
),
1478+
when(
1479+
ne(cat.e(), int(CUSTOM)),
1480+
vec![type_error(add(
1481+
quoted(type_name(x.e())),
1482+
text(" object is not subscriptable"),
1483+
))],
1484+
),
1485+
when(
1486+
is_dict(x.e()),
1487+
vec![ret(call(
1488+
"zb_dict_get",
1489+
vec![raw_any(x.e()), box_i64(position.e())],
1490+
any(),
1491+
))],
1492+
),
1493+
when(
1494+
kind_is(Kind::Int, x.e()),
1495+
vec![ret(box_i64(call(
1496+
"zb_list_get_i64",
1497+
vec![unbox(Kind::Int, x.e()), position.e()],
1498+
i64(),
1499+
)))],
1500+
),
1501+
when(
1502+
kind_is(Kind::Float, x.e()),
1503+
vec![ret(box_f64(call(
1504+
"zb_list_get_f64",
1505+
vec![unbox(Kind::Float, x.e()), position.e()],
1506+
f64(),
1507+
)))],
1508+
),
1509+
when(
1510+
kind_is(Kind::Str, x.e()),
1511+
vec![ret(box_str(call(
1512+
"zb_list_get_str",
1513+
vec![unbox(Kind::Str, x.e()), position.e()],
1514+
string(),
1515+
)))],
1516+
),
1517+
when(
1518+
kind_is(Kind::Ptr, x.e()),
1519+
vec![ret(call(
1520+
"zb_hook_box_instance",
1521+
vec![call(
1522+
"zb_list_get_ptr",
1523+
vec![unbox(Kind::Ptr, x.e()), position.e()],
1524+
Kind::Ptr.ty(),
1525+
)],
1526+
any(),
1527+
))],
1528+
),
1529+
ret(call(
1530+
"zb_list_get_any",
1531+
vec![iter(x.e()), position.e()],
1532+
any(),
1533+
)),
1534+
],
1535+
));
14591536
let mutable_list = |x: Expr| {
14601537
and(
14611538
eq(category(x.clone()), int(CUSTOM)),
@@ -1567,6 +1644,99 @@ pub(crate) fn declarations(policy: &Policy, list_type: TypeId) -> Vec<Decl> {
15671644
ret_void(),
15681645
],
15691646
));
1647+
let list_set_i64 = |k: Kind, x: Expr, i: Expr, v: Expr| {
1648+
expr(call(
1649+
&format!("zb_list_set_{}", k.suffix()),
1650+
vec![unbox(k, x), i, v],
1651+
unit(),
1652+
))
1653+
};
1654+
d.push(define(
1655+
"zb_any_setitem_i64",
1656+
&[&x, &position, &v],
1657+
unit(),
1658+
vec![
1659+
when(
1660+
boxed_dict(x.e()),
1661+
vec![
1662+
expr(call(
1663+
"zb_dict_set",
1664+
vec![raw_any(x.e()), box_i64(position.e()), v.e()],
1665+
unit(),
1666+
)),
1667+
ret_void(),
1668+
],
1669+
),
1670+
when(
1671+
not(mutable_list(x.e())),
1672+
vec![type_error(add(
1673+
quoted(type_name(x.e())),
1674+
text(" object does not support item assignment"),
1675+
))],
1676+
),
1677+
when(
1678+
kind_is(Kind::Int, x.e()),
1679+
vec![
1680+
list_set_i64(
1681+
Kind::Int,
1682+
x.e(),
1683+
position.e(),
1684+
call("zb_any_as_i64", vec![v.e()], i64()),
1685+
),
1686+
ret_void(),
1687+
],
1688+
),
1689+
when(
1690+
kind_is(Kind::Float, x.e()),
1691+
vec![
1692+
list_set_i64(
1693+
Kind::Float,
1694+
x.e(),
1695+
position.e(),
1696+
call("zb_any_as_f64", vec![v.e()], f64()),
1697+
),
1698+
ret_void(),
1699+
],
1700+
),
1701+
when(
1702+
kind_is(Kind::Str, x.e()),
1703+
vec![
1704+
list_set_i64(
1705+
Kind::Str,
1706+
x.e(),
1707+
position.e(),
1708+
call("zb_any_as_str", vec![v.e()], string()),
1709+
),
1710+
ret_void(),
1711+
],
1712+
),
1713+
when(
1714+
kind_is(Kind::Ptr, x.e()),
1715+
vec![
1716+
when(
1717+
not(is_instance(v.e())),
1718+
vec![type_error(add(
1719+
text("expected an instance, got "),
1720+
quoted(type_name(v.e())),
1721+
))],
1722+
),
1723+
list_set_i64(
1724+
Kind::Ptr,
1725+
x.e(),
1726+
position.e(),
1727+
cast(call("zb_unbox_instance_raw", vec![v.e()], i64()), usize()),
1728+
),
1729+
ret_void(),
1730+
],
1731+
),
1732+
expr(call(
1733+
"zb_list_set_any",
1734+
vec![unbox(Kind::Any, x.e()), position.e(), v.e()],
1735+
unit(),
1736+
)),
1737+
ret_void(),
1738+
],
1739+
));
15701740
d.push(define(
15711741
"zb_any_delitem",
15721742
&[&x, &i],
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
1 2 [1, 2]
2+
3 4.5 five
3+
20
4+
20.5
5+
a
6+
two
7+
7
8+
7.5
9+
x
10+
zero
11+
[9.5, 2.5]
12+
{0: 9.5}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
def record(events, value):
2+
events.append(value)
3+
return value
4+
5+
6+
events = []
7+
left, right = (record(events, 1), record(events, 2))
8+
print(left, right, events)
9+
10+
first, (second, third) = (record(events, 3), (4.5, "five"))
11+
print(first, second, third)
12+
13+
def get_first(value):
14+
return value[0]
15+
16+
def last(value):
17+
return value[-1]
18+
19+
def get_second(value):
20+
return value[1]
21+
22+
23+
def dict_item(value):
24+
return value[2]
25+
26+
27+
print(last((10, 20)))
28+
print(get_second([10.5, 20.5]))
29+
print(get_first("abc"))
30+
print(dict_item({2: "two"}))
31+
32+
def dynamic_zero(value):
33+
return value[0]
34+
35+
36+
print(dynamic_zero((7, 8)))
37+
print(dynamic_zero([7.5, 8.5]))
38+
print(dynamic_zero("xyz"))
39+
print(dynamic_zero({0: "zero"}))
40+
41+
def replace_zero(value):
42+
value[0] = 9.5
43+
return value
44+
45+
46+
print(replace_zero([1.5, 2.5]))
47+
print(replace_zero({0: 1.5}))

‎crates/zyntax_python/src/lower.rs‎

Lines changed: 54 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2107,6 +2107,34 @@ impl<'m> Lowerer<'m> {
21072107
{
21082108
return Ok(());
21092109
}
2110+
// A tuple literal consumed immediately by an unpacking
2111+
// assignment has no observable tuple identity. Evaluate
2112+
// every RHS expression first, then bind the targets in
2113+
// order, retaining each element's inferred type.
2114+
if let ([target], py::Expr::Tuple(source)) = (a.targets.as_slice(), &*a.value) {
2115+
let targets = match target {
2116+
py::Expr::Tuple(t) => Some(&t.elts),
2117+
py::Expr::List(l) => Some(&l.elts),
2118+
_ => None,
2119+
};
2120+
if let Some(targets) = targets.filter(|t| {
2121+
t.len() == source.elts.len()
2122+
&& t.iter().all(|target| {
2123+
!matches!(target, py::Expr::Tuple(_) | py::Expr::List(_))
2124+
})
2125+
}) {
2126+
let mut values = Vec::with_capacity(source.elts.len());
2127+
for expr in &source.elts {
2128+
let value = self.expr(expr)?;
2129+
out.append(&mut self.hoisted);
2130+
values.push(self.hold(value, out, span));
2131+
}
2132+
for (target, value) in targets.iter().zip(values) {
2133+
self.bind(target, value, span, out)?;
2134+
}
2135+
return Ok(());
2136+
}
2137+
}
21102138
// A literal that says nothing of its elements is built
21112139
// as the list its name or field holds, which inference
21122140
// typed by what the program puts in it.
@@ -2695,9 +2723,19 @@ impl<'m> Lowerer<'m> {
26952723
)
26962724
}
26972725
Ty::Object => {
2698-
let i = self.expr_as(&sub.slice, Ty::Object)?;
2726+
let i = self.expr(&sub.slice)?;
26992727
let v = self.coerce(value, Ty::Object);
2700-
call("zb_any_setitem", vec![seq.node, i, v], Ty::None, span)
2728+
if i.ty == Ty::Int {
2729+
call(
2730+
"zb_any_setitem_i64",
2731+
vec![seq.node, i.node, v],
2732+
Ty::None,
2733+
span,
2734+
)
2735+
} else {
2736+
let i = self.coerce(i, Ty::Object);
2737+
call("zb_any_setitem", vec![seq.node, i, v], Ty::None, span)
2738+
}
27012739
}
27022740
_ => {
27032741
return unsupported(
@@ -2915,16 +2953,12 @@ impl<'m> Lowerer<'m> {
29152953
Ty::List(e) => elem_call("get", e, vec![seq.node, index], span),
29162954
Ty::Tuple => call("zb_list_get_any", vec![seq.node, index], Ty::Object, span),
29172955
Ty::Str => call("zb_str_get", vec![seq.node, index], Ty::Str, span),
2918-
_ => {
2919-
let i = self.coerce(
2920-
Val {
2921-
node: index,
2922-
ty: Ty::Int,
2923-
},
2924-
Ty::Object,
2925-
);
2926-
call("zb_any_getitem", vec![seq.node, i], Ty::Object, span)
2927-
}
2956+
_ => call(
2957+
"zb_any_getitem_i64",
2958+
vec![seq.node, index],
2959+
Ty::Object,
2960+
span,
2961+
),
29282962
};
29292963
Val { node, ty: elem_ty }
29302964
}
@@ -4922,10 +4956,16 @@ impl<'m> Lowerer<'m> {
49224956
})
49234957
}
49244958
_ => {
4925-
let key = self.expr_as(&sub.slice, Ty::Object)?;
4959+
let key = self.expr(&sub.slice)?;
49264960
let o = self.coerce(seq, Ty::Object);
4961+
let node = if key.ty == Ty::Int {
4962+
call("zb_any_getitem_i64", vec![o, key.node], Ty::Object, span)
4963+
} else {
4964+
let key = self.coerce(key, Ty::Object);
4965+
call("zb_any_getitem", vec![o, key], Ty::Object, span)
4966+
};
49274967
Ok(Val {
4928-
node: call("zb_any_getitem", vec![o, key], Ty::Object, span),
4968+
node,
49294969
ty: Ty::Object,
49304970
})
49314971
}

‎crates/zyntax_python/src/types.rs‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3358,6 +3358,34 @@ impl Walker<'_> {
33583358
}
33593359
}
33603360

3361+
/// A tuple literal gives each unpacked name the type of its own
3362+
/// expression. No tuple escapes this assignment, so the lowering can
3363+
/// bind those values directly without putting them through `Any`.
3364+
fn target_tuple_literal(&mut self, target: &py::Expr, value: &py::Expr) -> bool {
3365+
let py::Expr::Tuple(source) = value else {
3366+
return false;
3367+
};
3368+
let targets = match target {
3369+
py::Expr::Tuple(t) => &t.elts,
3370+
py::Expr::List(l) => &l.elts,
3371+
_ => return false,
3372+
};
3373+
if targets.len() != source.elts.len() {
3374+
return false;
3375+
}
3376+
if targets
3377+
.iter()
3378+
.any(|target| matches!(target, py::Expr::Tuple(_) | py::Expr::List(_)))
3379+
{
3380+
return false;
3381+
}
3382+
for (target, value) in targets.iter().zip(&source.elts) {
3383+
let ty = self.expr(value);
3384+
self.target(target, ty);
3385+
}
3386+
true
3387+
}
3388+
33613389
fn stmts(&mut self, stmts: &[py::Stmt]) {
33623390
for s in stmts {
33633391
self.stmt(s);
@@ -3369,6 +3397,9 @@ impl Walker<'_> {
33693397
py::Stmt::Assign(a) => {
33703398
let ty = self.expr(&a.value);
33713399
for t in &a.targets {
3400+
if a.targets.len() == 1 && self.target_tuple_literal(t, &a.value) {
3401+
continue;
3402+
}
33723403
// An unkinded literal is the list its writes make
33733404
// it, decided afresh from the types known now.
33743405
if let py::Expr::Name(n) = t {

0 commit comments

Comments
 (0)