diff options
author | Vincent Ambo <mail@tazj.in> | 2022-09-24T12·42+0300 |
---|---|---|
committer | tazjin <tazjin@tvl.su> | 2022-09-29T11·47+0000 |
commit | 949897651e826598f2011611e0cc03619426fcc2 (patch) | |
tree | a0512b521c43a15ac451859ecb1d292bbf3d83f1 /tvix | |
parent | b593f6922c3b31f2456cb8885d80af3789a380b3 (diff) |
feat(tvix/eval): implement dynamic keys in recursive attrs r/4988
This wires up the new bindings setup logic to be able to thread through & compile dynamic attributes in recursive attrs. It seems like we don't actually need to retain the phasing of Nix exactly, as we can use the phantom mechanism to declare all locals without making the dynamic ones accessible. Change-Id: Ic2d43dd8fd97d7ccd56d8c6adf2ff97274cd837a Reviewed-on: https://cl.tvl.fyi/c/depot/+/6781 Reviewed-by: sterni <sternenseemann@systemli.org> Tested-by: BuildkiteCI
Diffstat (limited to 'tvix')
-rw-r--r-- | tvix/eval/src/compiler/bindings.rs | 62 | ||||
-rw-r--r-- | tvix/eval/src/tests/tvix_tests/eval-okay-rec-dynamic-keys.exp | 1 | ||||
-rw-r--r-- | tvix/eval/src/tests/tvix_tests/eval-okay-rec-dynamic-keys.nix | 5 |
3 files changed, 39 insertions, 29 deletions
diff --git a/tvix/eval/src/compiler/bindings.rs b/tvix/eval/src/compiler/bindings.rs index 94dae5fb951a..86245270f290 100644 --- a/tvix/eval/src/compiler/bindings.rs +++ b/tvix/eval/src/compiler/bindings.rs @@ -22,14 +22,14 @@ enum Binding { enum KeySlot { /// There is no key slot (`let`-expressions do not emit their key). - None, + None { name: SmolStr }, /// The key is statically known and has a slot. Static { slot: LocalIdx, name: SmolStr }, /// The key is dynamic, i.e. only known at runtime, and must be compiled /// into its slot. - Dynamic { slot: LocalIdx, expr: ast::Expr }, + Dynamic { slot: LocalIdx, attr: ast::Attr }, } struct TrackedBinding { @@ -180,7 +180,7 @@ impl Compiler<'_> { name: name.clone(), } } else { - KeySlot::None + KeySlot::None { name: name.clone() } }; let value_slot = match kind { @@ -221,22 +221,25 @@ impl Compiler<'_> { for entry in node.attrpath_values() { *count += 1; - let path = entry.attrpath().unwrap().attrs().collect::<Vec<_>>(); + let mut path = entry.attrpath().unwrap().attrs().collect::<Vec<_>>(); if path.len() != 1 { self.emit_error(&entry, ErrorKind::NotImplemented("nested bindings :(")); continue; } - let name = match self.expr_static_attr_str(&path[0]) { - Some(name) => name, + let key_span = self.span_for(&path[0]); + let key_slot = match self.expr_static_attr_str(&path[0]) { + Some(name) if kind.is_attrs() => KeySlot::Static { + name, + slot: self.scope_mut().declare_phantom(key_span, false), + }, - None if kind.is_attrs() => { - self.emit_error( - &entry, - ErrorKind::NotImplemented("dynamic keys in `rec` sets"), - ); - continue; - } + Some(name) => KeySlot::None { name }, + + None if kind.is_attrs() => KeySlot::Dynamic { + attr: path.pop().unwrap(), + slot: self.scope_mut().declare_phantom(key_span, false), + }, None => { self.emit_error(&path[0], ErrorKind::DynamicKeyInScope("let-expression")); @@ -244,20 +247,20 @@ impl Compiler<'_> { } }; - let key_span = self.span_for(&path[0]); - let key_slot = if kind.is_attrs() { - KeySlot::Static { - name: name.clone(), - slot: self.scope_mut().declare_phantom(key_span, false), - } - } else { - KeySlot::None - }; - let value_slot = match kind { - // In recursive scopes, the value needs to be accessible on the - // stack. - BindingsKind::LetIn | BindingsKind::RecAttrs => self.declare_local(&key_span, name), + BindingsKind::LetIn | BindingsKind::RecAttrs => match &key_slot { + // In recursive scopes, the value needs to be accessible on the + // stack if it is statically known + KeySlot::None { name } | KeySlot::Static { name, .. } => { + self.declare_local(&key_span, name.as_str()) + } + + // Dynamic values are never resolvable (as their names are + // of course only known at runtime). + // + // Note: This branch is unreachable in `let`-expressions. + KeySlot::Dynamic { .. } => self.scope_mut().declare_phantom(key_span, false), + }, // In non-recursive attribute sets, the value is inaccessible // (only consumed by `OpAttrs`). @@ -439,7 +442,7 @@ impl Compiler<'_> { value_indices.push(binding.value_slot); match binding.key_slot { - KeySlot::None => {} // nothing to do here + KeySlot::None { .. } => {} // nothing to do here KeySlot::Static { slot, name } => { let span = self.scope()[slot].span; @@ -447,8 +450,9 @@ impl Compiler<'_> { self.scope_mut().mark_initialised(slot); } - KeySlot::Dynamic { .. } => { - todo!("dynamic keys not ye timplemented") + KeySlot::Dynamic { slot, attr } => { + self.compile_attr(slot, attr); + self.scope_mut().mark_initialised(slot); } } diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-rec-dynamic-keys.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-rec-dynamic-keys.exp new file mode 100644 index 000000000000..ac8d062a6911 --- /dev/null +++ b/tvix/eval/src/tests/tvix_tests/eval-okay-rec-dynamic-keys.exp @@ -0,0 +1 @@ +{ barbaz = 42; foobar = 42; val = 21; } diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-rec-dynamic-keys.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-rec-dynamic-keys.nix new file mode 100644 index 000000000000..8d7a8cef8e10 --- /dev/null +++ b/tvix/eval/src/tests/tvix_tests/eval-okay-rec-dynamic-keys.nix @@ -0,0 +1,5 @@ +rec { + val = 21; + ${"foo" + "bar"} = 42; + ${"bar" + "baz"} = val * 2; +} |