refactor(tvix/eval): statically resolve select from constant attrs
When resolving a select expression (`attrs.name` or `attrs.name or default`), if the set compiles to a constant attribute set (as is most notably the case with `builtins`) we can backtrack and replace that attribute set directly with the compiled value. For something like `builtins.length`, this will directly emit an `OpConstant` that leaves the `length` builtin on the stack. Change-Id: I639654e065a06e8cfcbcacb528c6da7ec9e513ee Reviewed-on: https://cl.tvl.fyi/c/depot/+/7957 Tested-by: BuildkiteCI Reviewed-by: flokli <flokli@flokli.de>
This commit is contained in:
parent
f2afd38f2d
commit
32698766ef
3 changed files with 94 additions and 50 deletions
|
|
@ -226,7 +226,7 @@ impl TrackedBindings {
|
|||
|
||||
// If the first element of the path is not statically known, the entry
|
||||
// can not be merged.
|
||||
let name = match c.expr_static_attr_str(name) {
|
||||
let name = match expr_static_attr_str(name) {
|
||||
Some(name) => name,
|
||||
None => return false,
|
||||
};
|
||||
|
|
@ -336,7 +336,7 @@ impl Compiler<'_> {
|
|||
|
||||
None => {
|
||||
for attr in inherit.attrs() {
|
||||
let name = match self.expr_static_attr_str(&attr) {
|
||||
let name = match expr_static_attr_str(&attr) {
|
||||
Some(name) => name,
|
||||
None => {
|
||||
self.emit_error(&attr, ErrorKind::DynamicKeyInScope("inherit"));
|
||||
|
|
@ -387,7 +387,7 @@ impl Compiler<'_> {
|
|||
|
||||
Some(from) => {
|
||||
for attr in inherit.attrs() {
|
||||
let name = match self.expr_static_attr_str(&attr) {
|
||||
let name = match expr_static_attr_str(&attr) {
|
||||
Some(name) => name,
|
||||
None => {
|
||||
self.emit_error(&attr, ErrorKind::DynamicKeyInScope("inherit"));
|
||||
|
|
@ -476,7 +476,7 @@ impl Compiler<'_> {
|
|||
*count += 1;
|
||||
|
||||
let key_span = self.span_for(&key);
|
||||
let key_slot = match self.expr_static_attr_str(&key) {
|
||||
let key_slot = match expr_static_attr_str(&key) {
|
||||
Some(name) if kind.is_attrs() => KeySlot::Static {
|
||||
name,
|
||||
slot: self.scope_mut().declare_phantom(key_span, false),
|
||||
|
|
@ -819,37 +819,4 @@ impl Compiler<'_> {
|
|||
self.contexts[ctx_idx].lambda.upvalue_count += 1;
|
||||
idx
|
||||
}
|
||||
|
||||
/// Convert a non-dynamic string expression to a string if possible.
|
||||
fn expr_static_str(&self, node: &ast::Str) -> Option<SmolStr> {
|
||||
let mut parts = node.normalized_parts();
|
||||
|
||||
if parts.len() != 1 {
|
||||
return None;
|
||||
}
|
||||
|
||||
if let Some(ast::InterpolPart::Literal(lit)) = parts.pop() {
|
||||
return Some(SmolStr::new(lit));
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// Convert the provided `ast::Attr` into a statically known string if
|
||||
/// possible.
|
||||
fn expr_static_attr_str(&self, node: &ast::Attr) -> Option<SmolStr> {
|
||||
match node {
|
||||
ast::Attr::Ident(ident) => Some(ident.ident_token().unwrap().text().into()),
|
||||
ast::Attr::Str(s) => self.expr_static_str(s),
|
||||
|
||||
// The dynamic node type is just a wrapper. C++ Nix does not care
|
||||
// about the dynamic wrapper when determining whether the node
|
||||
// itself is dynamic, it depends solely on the expression inside
|
||||
// (i.e. `let ${"a"} = 1; in a` is valid).
|
||||
ast::Attr::Dynamic(ref dynamic) => match dynamic.expr().unwrap() {
|
||||
ast::Expr::Str(s) => self.expr_static_str(&s),
|
||||
_ => None,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue