From 63a9eddc49735e9136a6cde78cdc772d9e7ad259 Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Sun, 22 Feb 2026 00:16:42 +0300 Subject: [PATCH] irc/evaluator: fix `e` passed by value in Thunk constructor Signed-off-by: NotAShelf Change-Id: If3bdfd1fc0851d4113b89827474a74a86a6a6964 --- src/irc/evaluator.cpp | 854 ++++++++++++++++++++---------------------- 1 file changed, 415 insertions(+), 439 deletions(-) diff --git a/src/irc/evaluator.cpp b/src/irc/evaluator.cpp index 977ea85..dfd6eb1 100644 --- a/src/irc/evaluator.cpp +++ b/src/irc/evaluator.cpp @@ -7,481 +7,457 @@ #include "nix/expr/value.hh" #include "nix/util/error.hh" -#include #include +#include namespace nix_irc { using namespace nix; struct IREnvironment { - IREnvironment* parent; - std::vector bindings; - Value* with_attrs; + IREnvironment* parent; + std::vector bindings; + Value* with_attrs; - explicit IREnvironment(IREnvironment* p = nullptr) : parent(p), with_attrs(nullptr) {} + explicit IREnvironment(IREnvironment* p = nullptr) : parent(p), with_attrs(nullptr) {} - void bind(Value* val) { - bindings.push_back(val); + void bind(Value* val) { bindings.push_back(val); } + + Value* lookup(uint32_t index) { + IREnvironment* env = this; + while (env) { + if (index < env->bindings.size()) { + return env->bindings[index]; + } + index -= env->bindings.size(); + env = env->parent; } + return nullptr; + } - Value* lookup(uint32_t index) { - IREnvironment* env = this; - while (env) { - if (index < env->bindings.size()) { - return env->bindings[index]; - } - index -= env->bindings.size(); - env = env->parent; + Value* lookup_with(EvalState& state, const std::string& name) { + IREnvironment* env = this; + while (env) { + if (env->with_attrs && env->with_attrs->type() == nAttrs) { + auto sym = state.symbols.create(name); + auto attr = env->with_attrs->attrs()->get(sym); + if (attr) { + return attr->value; } - return nullptr; - } - - Value* lookup_with(EvalState& state, const std::string& name) { - IREnvironment* env = this; - while (env) { - if (env->with_attrs && env->with_attrs->type() == nAttrs) { - auto sym = state.symbols.create(name); - auto attr = env->with_attrs->attrs()->get(sym); - if (attr) { - return attr->value; - } - } - env = env->parent; - } - return nullptr; + } + env = env->parent; } + return nullptr; + } }; struct Thunk { - std::shared_ptr expr; - IREnvironment* env; - bool blackholed; + std::shared_ptr expr; + IREnvironment* env; + bool blackholed; - Thunk(std::shared_ptr e, IREnvironment* en) - : expr(e), env(en), blackholed(false) {} + Thunk(const std::shared_ptr& e, IREnvironment* en) : expr(e), env(en), blackholed(false) {} }; struct Evaluator::Impl { - EvalState& state; - std::unordered_map> thunks; - std::vector> environments; + EvalState& state; + std::unordered_map> thunks; + std::vector> environments; - explicit Impl(EvalState& s) : state(s) {} + explicit Impl(EvalState& s) : state(s) {} - ~Impl() { - for (auto& env : environments) { - delete env.release(); - } + ~Impl() { + for (auto& env : environments) { + delete env.release(); + } + } + + IREnvironment* make_env(IREnvironment* parent = nullptr) { + auto env = new IREnvironment(parent); + environments.push_back(std::unique_ptr(env)); + return env; + } + + Value* make_thunk(const std::shared_ptr& expr, IREnvironment* env) { + Value* v = state.allocValue(); + thunks[v] = std::make_unique(expr, env); + return v; + } + + void force(Value* v) { + auto it = thunks.find(v); + if (it == thunks.end()) { + return; } - IREnvironment* make_env(IREnvironment* parent = nullptr) { - auto env = new IREnvironment(parent); - environments.push_back(std::unique_ptr(env)); - return env; + Thunk* thunk = it->second.get(); + if (thunk->blackholed) { + state.error("infinite recursion encountered").debugThrow(); } - Value* make_thunk(const std::shared_ptr& expr, IREnvironment* env) { - Value* v = state.allocValue(); - thunks[v] = std::make_unique(expr, env); - return v; + thunk->blackholed = true; + eval_node(thunk->expr, *v, thunk->env); + thunks.erase(v); + } + + void eval_node(const std::shared_ptr& node, Value& v, IREnvironment* env) { + if (!node) { + v.mkNull(); + return; } - void force(Value* v) { - auto it = thunks.find(v); - if (it == thunks.end()) { - return; - } + if (auto* n = node->get_if()) { + v.mkInt(n->value); + } else if (auto* n = node->get_if()) { + v.mkString(n->value); + } else if (auto* n = node->get_if()) { + v.mkPath(state.rootPath(CanonPath(n->value))); + } else if (auto* n = node->get_if()) { + v.mkBool(n->value); + } else if (auto* n = node->get_if()) { // NOLINT(bugprone-branch-clone) + v.mkNull(); + } else if (auto* n = node->get_if()) { + Value* bound = env ? env->lookup(n->index) : nullptr; + if (!bound && env && n->name.has_value()) { + bound = env->lookup_with(state, n->name.value()); + } + if (!bound) { + state.error("variable not found").debugThrow(); + } + force(bound); + v = *bound; + } else if (auto* n = node->get_if()) { + auto lambda_env = env; + auto body = n->body; - Thunk* thunk = it->second.get(); - if (thunk->blackholed) { - state.error("infinite recursion encountered").debugThrow(); - } + auto primOp = [this, lambda_env, body](EvalState& state, PosIdx pos, Value** args, + Value& result) { + auto call_env = make_env(lambda_env); + call_env->bind(args[0]); + eval_node(body, result, call_env); + }; - thunk->blackholed = true; - eval_node(thunk->expr, *v, thunk->env); - thunks.erase(v); - } - - void eval_node(const std::shared_ptr& node, Value& v, IREnvironment* env) { - if (!node) { - v.mkNull(); - return; - } - - if (auto* n = node->get_if()) { - v.mkInt(n->value); - } - else if (auto* n = node->get_if()) { - v.mkString(n->value); - } - else if (auto* n = node->get_if()) { - v.mkPath(state.rootPath(CanonPath(n->value))); - } - else if (auto* n = node->get_if()) { - v.mkBool(n->value); - } - else if (auto* n = node->get_if()) { - v.mkNull(); - } - else if (auto* n = node->get_if()) { - Value* bound = env ? env->lookup(n->index) : nullptr; - if (!bound && env && n->name.has_value()) { - bound = env->lookup_with(state, n->name.value()); - } - if (!bound) { - state.error("variable not found").debugThrow(); - } - force(bound); - v = *bound; - } - else if (auto* n = node->get_if()) { - auto lambda_env = env; - auto body = n->body; - - auto primOp = [this, lambda_env, body](EvalState& state, PosIdx pos, - Value** args, Value& result) { - auto call_env = make_env(lambda_env); - call_env->bind(args[0]); - eval_node(body, result, call_env); - }; - - v.mkPrimOp(new PrimOp { - .name = n->param_name.value_or("lambda"), - .arity = static_cast(n->arity), - .fun = primOp - }); - } - else if (auto* n = node->get_if()) { - Value* func_val = state.allocValue(); - eval_node(n->func, *func_val, env); - force(func_val); - - Value* arg_val = make_thunk(n->arg, env); - - state.callFunction(*func_val, *arg_val, v, noPos); - } - else if (auto* n = node->get_if()) { - Value* left = state.allocValue(); - Value* right = state.allocValue(); - - switch (n->op) { - case BinaryOp::AND: - eval_node(n->left, *left, env); - force(left); - if (left->type() != nBool) { - state.error("type error in logical AND").debugThrow(); - } - if (!left->boolean()) { - v.mkBool(false); - } else { - eval_node(n->right, *right, env); - force(right); - if (right->type() != nBool) { - state.error("type error in logical AND").debugThrow(); - } - v.mkBool(right->boolean()); - } - break; - case BinaryOp::OR: - eval_node(n->left, *left, env); - force(left); - if (left->type() != nBool) { - state.error("type error in logical OR").debugThrow(); - } - if (left->boolean()) { - v.mkBool(true); - } else { - eval_node(n->right, *right, env); - force(right); - if (right->type() != nBool) { - state.error("type error in logical OR").debugThrow(); - } - v.mkBool(right->boolean()); - } - break; - case BinaryOp::IMPL: - eval_node(n->left, *left, env); - force(left); - if (left->type() != nBool) { - state.error("type error in implication").debugThrow(); - } - if (!left->boolean()) { - v.mkBool(true); - } else { - eval_node(n->right, *right, env); - force(right); - if (right->type() != nBool) { - state.error("type error in implication").debugThrow(); - } - v.mkBool(right->boolean()); - } - break; - default: - eval_node(n->left, *left, env); - eval_node(n->right, *right, env); - force(left); - force(right); - - switch (n->op) { - case BinaryOp::ADD: - if (left->type() == nInt && right->type() == nInt) { - v.mkInt((left->integer() + right->integer()).valueWrapping()); - } else if (left->type() == nString && right->type() == nString) { - v.mkString(std::string(left->c_str()) + std::string(right->c_str())); - } else { - state.error("type error in addition").debugThrow(); - } - break; - case BinaryOp::SUB: - if (left->type() == nInt && right->type() == nInt) { - v.mkInt((left->integer() - right->integer()).valueWrapping()); - } else { - state.error("type error in subtraction").debugThrow(); - } - break; - case BinaryOp::MUL: - if (left->type() == nInt && right->type() == nInt) { - v.mkInt((left->integer() * right->integer()).valueWrapping()); - } else { - state.error("type error in multiplication").debugThrow(); - } - break; - case BinaryOp::DIV: - if (left->type() == nInt && right->type() == nInt) { - if (right->integer() == NixInt(0)) { - state.error("division by zero").debugThrow(); - } - v.mkInt((left->integer() / right->integer()).valueWrapping()); - } else { - state.error("type error in division").debugThrow(); - } - break; - case BinaryOp::EQ: - v.mkBool(state.eqValues(*left, *right, noPos, "")); - break; - case BinaryOp::NE: - v.mkBool(!state.eqValues(*left, *right, noPos, "")); - break; - case BinaryOp::LT: - if (left->type() == nInt && right->type() == nInt) { - v.mkBool(left->integer() < right->integer()); - } else if (left->type() == nString && right->type() == nString) { - v.mkBool(std::string(left->c_str()) < std::string(right->c_str())); - } else { - state.error("type error in comparison").debugThrow(); - } - break; - case BinaryOp::GT: - if (left->type() == nInt && right->type() == nInt) { - v.mkBool(left->integer() > right->integer()); - } else if (left->type() == nString && right->type() == nString) { - v.mkBool(std::string(left->c_str()) > std::string(right->c_str())); - } else { - state.error("type error in comparison").debugThrow(); - } - break; - case BinaryOp::LE: - if (left->type() == nInt && right->type() == nInt) { - v.mkBool(left->integer() <= right->integer()); - } else if (left->type() == nString && right->type() == nString) { - v.mkBool(std::string(left->c_str()) <= std::string(right->c_str())); - } else { - state.error("type error in comparison").debugThrow(); - } - break; - case BinaryOp::GE: - if (left->type() == nInt && right->type() == nInt) { - v.mkBool(left->integer() >= right->integer()); - } else if (left->type() == nString && right->type() == nString) { - v.mkBool(std::string(left->c_str()) >= std::string(right->c_str())); - } else { - state.error("type error in comparison").debugThrow(); - } - break; - case BinaryOp::CONCAT: - // ++ is list concatenation in Nix; string concat uses ADD (+) - state.error("list concatenation not yet implemented").debugThrow(); - break; - default: - state.error("unknown binary operator").debugThrow(); - } - break; - } - } - else if (auto* n = node->get_if()) { - Value* operand = state.allocValue(); - eval_node(n->operand, *operand, env); - force(operand); - - switch (n->op) { - case UnaryOp::NEG: - if (operand->type() == nInt) { - v.mkInt((NixInt(0) - operand->integer()).valueWrapping()); - } else { - state.error("type error in negation").debugThrow(); - } - break; - case UnaryOp::NOT: - if (operand->type() == nBool) { - v.mkBool(!operand->boolean()); - } else { - state.error("type error in logical NOT").debugThrow(); - } - break; - default: - state.error("unknown unary operator").debugThrow(); - } - } - else if (auto* n = node->get_if()) { - Value* cond = state.allocValue(); - eval_node(n->cond, *cond, env); - force(cond); - - if (cond->type() != nBool) { - state.error("condition must be a boolean").debugThrow(); - } - - if (cond->boolean()) { - eval_node(n->then_branch, v, env); - } else { - eval_node(n->else_branch, v, env); - } - } - else if (auto* n = node->get_if()) { - auto let_env = make_env(env); - for (const auto& [name, expr] : n->bindings) { - Value* val = make_thunk(expr, env); - let_env->bind(val); - } - eval_node(n->body, v, let_env); - } - else if (auto* n = node->get_if()) { - auto letrec_env = make_env(env); - std::vector thunk_vals; - - for (const auto& [name, expr] : n->bindings) { - Value* val = make_thunk(expr, letrec_env); - thunk_vals.push_back(val); - letrec_env->bind(val); - } - - eval_node(n->body, v, letrec_env); - } - else if (auto* n = node->get_if()) { - auto bindings = state.buildBindings(n->attrs.size()); - - IREnvironment* attr_env = env; - if (n->recursive) { - attr_env = make_env(env); - for (const auto& [key, val] : n->attrs) { - Value* thunk = make_thunk(val, attr_env); - attr_env->bind(thunk); - } - } - - for (const auto& [key, val] : n->attrs) { - Value* attr_val = state.allocValue(); - if (n->recursive) { - eval_node(val, *attr_val, attr_env); - } else { - eval_node(val, *attr_val, env); - } - bindings.insert(state.symbols.create(key), attr_val); - } - - v.mkAttrs(bindings.finish()); - } - else if (auto* n = node->get_if()) { - Value* obj = state.allocValue(); - eval_node(n->expr, *obj, env); - force(obj); - - Value* attr_val = state.allocValue(); - eval_node(n->attr, *attr_val, env); - force(attr_val); - - if (obj->type() != nAttrs) { - state.error("selection on non-attrset").debugThrow(); - } - - if (attr_val->type() != nString) { - state.error("attribute name must be string").debugThrow(); - } - - auto sym = state.symbols.create(attr_val->c_str()); - auto attr = obj->attrs()->get(sym); - - if (attr) { - Value* val = attr->value; - force(val); - v = *val; - } else if (n->default_expr) { - eval_node(*n->default_expr, v, env); - } else { - state.error("attribute not found").debugThrow(); - } - } - else if (auto* n = node->get_if()) { - Value* obj = state.allocValue(); - eval_node(n->expr, *obj, env); - force(obj); - - Value* attr_val = state.allocValue(); - eval_node(n->attr, *attr_val, env); - force(attr_val); - - if (obj->type() != nAttrs) { - v.mkBool(false); - } else if (attr_val->type() != nString) { - state.error("attribute name must be string").debugThrow(); - } else { - auto sym = state.symbols.create(attr_val->c_str()); - auto attr = obj->attrs()->get(sym); - v.mkBool(attr != nullptr); - } - } - else if (auto* n = node->get_if()) { - Value* attrs = state.allocValue(); - eval_node(n->attrs, *attrs, env); - force(attrs); - - if (attrs->type() != nAttrs) { - state.error("with expression requires attrset").debugThrow(); - } - - auto with_env = make_env(env); - with_env->with_attrs = attrs; - eval_node(n->body, v, with_env); - } - else if (auto* n = node->get_if()) { - Value* cond = state.allocValue(); - eval_node(n->cond, *cond, env); - force(cond); - - if (cond->type() != nBool) { - state.error("assertion must be boolean").debugThrow(); - } - - if (!cond->boolean()) { - state.error("assertion failed").debugThrow(); - } - - eval_node(n->body, v, env); - } - else { - v.mkNull(); - } + v.mkPrimOp(new PrimOp{.name = n->param_name.value_or("lambda"), + .arity = static_cast(n->arity), + .fun = primOp}); + } else if (auto* n = node->get_if()) { + Value* func_val = state.allocValue(); + eval_node(n->func, *func_val, env); + force(func_val); + + Value* arg_val = make_thunk(n->arg, env); + + state.callFunction(*func_val, *arg_val, v, noPos); + } else if (auto* n = node->get_if()) { + Value* left = state.allocValue(); + Value* right = state.allocValue(); + + switch (n->op) { + case BinaryOp::AND: + eval_node(n->left, *left, env); + force(left); + if (left->type() != nBool) { + state.error("type error in logical AND").debugThrow(); + } + if (!left->boolean()) { + v.mkBool(false); + } else { + eval_node(n->right, *right, env); + force(right); + if (right->type() != nBool) { + state.error("type error in logical AND").debugThrow(); + } + v.mkBool(right->boolean()); + } + break; + case BinaryOp::OR: + eval_node(n->left, *left, env); + force(left); + if (left->type() != nBool) { + state.error("type error in logical OR").debugThrow(); + } + if (left->boolean()) { + v.mkBool(true); + } else { + eval_node(n->right, *right, env); + force(right); + if (right->type() != nBool) { + state.error("type error in logical OR").debugThrow(); + } + v.mkBool(right->boolean()); + } + break; + case BinaryOp::IMPL: + eval_node(n->left, *left, env); + force(left); + if (left->type() != nBool) { + state.error("type error in implication").debugThrow(); + } + if (!left->boolean()) { + v.mkBool(true); + } else { + eval_node(n->right, *right, env); + force(right); + if (right->type() != nBool) { + state.error("type error in implication").debugThrow(); + } + v.mkBool(right->boolean()); + } + break; + default: + eval_node(n->left, *left, env); + eval_node(n->right, *right, env); + force(left); + force(right); + + switch (n->op) { + case BinaryOp::ADD: + if (left->type() == nInt && right->type() == nInt) { + v.mkInt((left->integer() + right->integer()).valueWrapping()); + } else if (left->type() == nString && right->type() == nString) { + v.mkString(std::string(left->c_str()) + std::string(right->c_str())); + } else { + state.error("type error in addition").debugThrow(); + } + break; + case BinaryOp::SUB: + if (left->type() == nInt && right->type() == nInt) { + v.mkInt((left->integer() - right->integer()).valueWrapping()); + } else { + state.error("type error in subtraction").debugThrow(); + } + break; + case BinaryOp::MUL: + if (left->type() == nInt && right->type() == nInt) { + v.mkInt((left->integer() * right->integer()).valueWrapping()); + } else { + state.error("type error in multiplication").debugThrow(); + } + break; + case BinaryOp::DIV: + if (left->type() == nInt && right->type() == nInt) { + if (right->integer() == NixInt(0)) { + state.error("division by zero").debugThrow(); + } + v.mkInt((left->integer() / right->integer()).valueWrapping()); + } else { + state.error("type error in division").debugThrow(); + } + break; + case BinaryOp::EQ: + v.mkBool(state.eqValues(*left, *right, noPos, "")); + break; + case BinaryOp::NE: + v.mkBool(!state.eqValues(*left, *right, noPos, "")); + break; + case BinaryOp::LT: + if (left->type() == nInt && right->type() == nInt) { + v.mkBool(left->integer() < right->integer()); + } else if (left->type() == nString && right->type() == nString) { + v.mkBool(std::string(left->c_str()) < std::string(right->c_str())); + } else { + state.error("type error in comparison").debugThrow(); + } + break; + case BinaryOp::GT: + if (left->type() == nInt && right->type() == nInt) { + v.mkBool(left->integer() > right->integer()); + } else if (left->type() == nString && right->type() == nString) { + v.mkBool(std::string(left->c_str()) > std::string(right->c_str())); + } else { + state.error("type error in comparison").debugThrow(); + } + break; + case BinaryOp::LE: + if (left->type() == nInt && right->type() == nInt) { + v.mkBool(left->integer() <= right->integer()); + } else if (left->type() == nString && right->type() == nString) { + v.mkBool(std::string(left->c_str()) <= std::string(right->c_str())); + } else { + state.error("type error in comparison").debugThrow(); + } + break; + case BinaryOp::GE: + if (left->type() == nInt && right->type() == nInt) { + v.mkBool(left->integer() >= right->integer()); + } else if (left->type() == nString && right->type() == nString) { + v.mkBool(std::string(left->c_str()) >= std::string(right->c_str())); + } else { + state.error("type error in comparison").debugThrow(); + } + break; + case BinaryOp::CONCAT: + // ++ is list concatenation in Nix; string concat uses ADD (+) + state.error("list concatenation not yet implemented").debugThrow(); + break; + default: + state.error("unknown binary operator").debugThrow(); + } + break; + } + } else if (auto* n = node->get_if()) { + Value* operand = state.allocValue(); + eval_node(n->operand, *operand, env); + force(operand); + + switch (n->op) { + case UnaryOp::NEG: + if (operand->type() == nInt) { + v.mkInt((NixInt(0) - operand->integer()).valueWrapping()); + } else { + state.error("type error in negation").debugThrow(); + } + break; + case UnaryOp::NOT: + if (operand->type() == nBool) { + v.mkBool(!operand->boolean()); + } else { + state.error("type error in logical NOT").debugThrow(); + } + break; + default: + state.error("unknown unary operator").debugThrow(); + } + } else if (auto* n = node->get_if()) { + Value* cond = state.allocValue(); + eval_node(n->cond, *cond, env); + force(cond); + + if (cond->type() != nBool) { + state.error("condition must be a boolean").debugThrow(); + } + + if (cond->boolean()) { + eval_node(n->then_branch, v, env); + } else { + eval_node(n->else_branch, v, env); + } + } else if (auto* n = node->get_if()) { + auto let_env = make_env(env); + for (const auto& [name, expr] : n->bindings) { + Value* val = make_thunk(expr, env); + let_env->bind(val); + } + eval_node(n->body, v, let_env); + } else if (auto* n = node->get_if()) { + auto letrec_env = make_env(env); + std::vector thunk_vals; + + for (const auto& [name, expr] : n->bindings) { + Value* val = make_thunk(expr, letrec_env); + thunk_vals.push_back(val); + letrec_env->bind(val); + } + + eval_node(n->body, v, letrec_env); + } else if (auto* n = node->get_if()) { + auto bindings = state.buildBindings(n->attrs.size()); + + IREnvironment* attr_env = env; + if (n->recursive) { + attr_env = make_env(env); + for (const auto& [key, val] : n->attrs) { + Value* thunk = make_thunk(val, attr_env); + attr_env->bind(thunk); + } + } + + for (const auto& [key, val] : n->attrs) { + Value* attr_val = state.allocValue(); + if (n->recursive) { + eval_node(val, *attr_val, attr_env); + } else { + eval_node(val, *attr_val, env); + } + bindings.insert(state.symbols.create(key), attr_val); + } + + v.mkAttrs(bindings.finish()); + } else if (auto* n = node->get_if()) { + Value* obj = state.allocValue(); + eval_node(n->expr, *obj, env); + force(obj); + + Value* attr_val = state.allocValue(); + eval_node(n->attr, *attr_val, env); + force(attr_val); + + if (obj->type() != nAttrs) { + state.error("selection on non-attrset").debugThrow(); + } + + if (attr_val->type() != nString) { + state.error("attribute name must be string").debugThrow(); + } + + auto sym = state.symbols.create(attr_val->c_str()); + auto attr = obj->attrs()->get(sym); + + if (attr) { + Value* val = attr->value; + force(val); + v = *val; + } else if (n->default_expr) { + eval_node(*n->default_expr, v, env); + } else { + state.error("attribute not found").debugThrow(); + } + } else if (auto* n = node->get_if()) { + Value* obj = state.allocValue(); + eval_node(n->expr, *obj, env); + force(obj); + + Value* attr_val = state.allocValue(); + eval_node(n->attr, *attr_val, env); + force(attr_val); + + if (obj->type() != nAttrs) { + v.mkBool(false); + } else if (attr_val->type() != nString) { + state.error("attribute name must be string").debugThrow(); + } else { + auto sym = state.symbols.create(attr_val->c_str()); + auto attr = obj->attrs()->get(sym); + v.mkBool(attr != nullptr); + } + } else if (auto* n = node->get_if()) { + Value* attrs = state.allocValue(); + eval_node(n->attrs, *attrs, env); + force(attrs); + + if (attrs->type() != nAttrs) { + state.error("with expression requires attrset").debugThrow(); + } + + auto with_env = make_env(env); + with_env->with_attrs = attrs; + eval_node(n->body, v, with_env); + } else if (auto* n = node->get_if()) { + Value* cond = state.allocValue(); + eval_node(n->cond, *cond, env); + force(cond); + + if (cond->type() != nBool) { + state.error("assertion must be boolean").debugThrow(); + } + + if (!cond->boolean()) { + state.error("assertion failed").debugThrow(); + } + + eval_node(n->body, v, env); + } else { + v.mkNull(); } + } }; Evaluator::Evaluator(EvalState& state) : pImpl(std::make_unique(state)) {} Evaluator::~Evaluator() = default; -void Evaluator::eval_to_nix(const std::shared_ptr& ir_node, - Value& result, +void Evaluator::eval_to_nix(const std::shared_ptr& ir_node, Value& result, IREnvironment* env) { - pImpl->eval_node(ir_node, result, env); + pImpl->eval_node(ir_node, result, env); } -} +} // namespace nix_irc