fix: handle __functor in forceFunction

This commit is contained in:
2026-01-31 18:06:53 +08:00
parent a08f0e78a3
commit 8f01ce2eb4
3 changed files with 46 additions and 21 deletions

View File

@@ -204,14 +204,32 @@ interface FixedOutputInfo {
hashMode: string;
}
const extractFixedOutputInfo = (attrs: NixAttrs): FixedOutputInfo | null => {
const extractFixedOutputInfo = (attrs: NixAttrs, ignoreNulls: boolean): FixedOutputInfo | null => {
if (!("outputHash" in attrs)) {
return null;
}
const hashValue = force(attrs.outputHash);
if (ignoreNulls && hashValue === null) {
return null;
}
const hash = forceStringValue(attrs.outputHash);
const hashAlgo = "outputHashAlgo" in attrs ? forceStringValue(attrs.outputHashAlgo) : "sha256";
const hashMode = "outputHashMode" in attrs ? forceStringValue(attrs.outputHashMode) : "flat";
let hashAlgo = "sha256";
if ("outputHashAlgo" in attrs) {
const algoValue = force(attrs.outputHashAlgo);
if (!(ignoreNulls && algoValue === null)) {
hashAlgo = forceStringValue(attrs.outputHashAlgo);
}
}
let hashMode = "flat";
if ("outputHashMode" in attrs) {
const modeValue = force(attrs.outputHashMode);
if (!(ignoreNulls && modeValue === null)) {
hashMode = forceStringValue(attrs.outputHashMode);
}
}
if (hashMode !== "flat" && hashMode !== "recursive") {
throw new Error(`derivation: invalid outputHashMode '${hashMode}' (must be 'flat' or 'recursive')`);
@@ -234,14 +252,13 @@ export const derivationStrict = (args: NixValue): NixAttrs => {
const builder = validateBuilder(attrs, collectedContext);
const platform = validateSystem(attrs);
const outputs = extractOutputs(attrs);
const fixedOutputInfo = extractFixedOutputInfo(attrs);
validateFixedOutputConstraints(fixedOutputInfo, outputs);
const structuredAttrs = "__structuredAttrs" in attrs ? force(attrs.__structuredAttrs) === true : false;
const ignoreNulls = "__ignoreNulls" in attrs ? force(attrs.__ignoreNulls) === true : false;
const outputs = extractOutputs(attrs);
const fixedOutputInfo = extractFixedOutputInfo(attrs, ignoreNulls);
validateFixedOutputConstraints(fixedOutputInfo, outputs);
if ("__contentAddressed" in attrs && force(attrs.__contentAddressed) === true) {
throw new Error("ca derivations are not supported");
}

View File

@@ -16,7 +16,6 @@ import type {
} from "./types";
import { isStringWithContext, isNixPath } from "./types";
import { force } from "./thunk";
import { getStringValue } from "./string-context";
import { isAttrs, isFunction, typeOf } from "./builtins/type-check";
/**
@@ -32,15 +31,25 @@ export const forceList = (value: NixValue): NixList => {
};
/**
* Force a value and assert it's a function
* @throws TypeError if value is not a function after forcing
* Force a value and assert it's a function or functor
* @throws TypeError if value is not a function or functor after forcing
*/
export const forceFunction = (value: NixValue): NixFunction => {
const forced = force(value);
if (!isFunction(forced)) {
throw new TypeError(`Expected function, got ${typeOf(forced)}`);
}
if (isFunction(forced)) {
return forced;
}
if (
typeof forced === "object" &&
!Array.isArray(forced) &&
forced !== null &&
"__functor" in forced
) {
const functorSet = forced as NixAttrs;
const functor = forceFunction(functorSet.__functor);
return (arg: NixValue) => forceFunction(functor(functorSet))(arg);
}
throw new TypeError(`Expected function, got ${typeOf(forced)}`);
};
/**

View File

@@ -473,15 +473,14 @@ impl<Ctx: CodegenContext> Compile<Ctx> for [(ExprId, ExprId)] {
let inner_ir = ctx.get_ir(inner);
let inner_span = inner_ir.span();
code!(buf, "let expr{}=Nix.createThunk(()=>(", slot.0);
inner_ir.compile(ctx, buf);
code!(
buf,
"),\"expr{} {}:{}:{}\");",
slot.0,
ctx.get_current_source().get_name(),
usize::from(inner_span.start()),
buf, ctx;
"let expr" slot.0 "=Nix.createThunk(()=>(" inner_ir "),"
"\"expr" slot.0 " "
ctx.get_current_source().get_name() ":"
usize::from(inner_span.start()) ":"
usize::from(inner_span.end())
"\");"
);
}
}