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; hashMode: string;
} }
const extractFixedOutputInfo = (attrs: NixAttrs): FixedOutputInfo | null => { const extractFixedOutputInfo = (attrs: NixAttrs, ignoreNulls: boolean): FixedOutputInfo | null => {
if (!("outputHash" in attrs)) { if (!("outputHash" in attrs)) {
return null; return null;
} }
const hashValue = force(attrs.outputHash);
if (ignoreNulls && hashValue === null) {
return null;
}
const hash = forceStringValue(attrs.outputHash); 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") { if (hashMode !== "flat" && hashMode !== "recursive") {
throw new Error(`derivation: invalid outputHashMode '${hashMode}' (must be 'flat' or '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 builder = validateBuilder(attrs, collectedContext);
const platform = validateSystem(attrs); 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 structuredAttrs = "__structuredAttrs" in attrs ? force(attrs.__structuredAttrs) === true : false;
const ignoreNulls = "__ignoreNulls" in attrs ? force(attrs.__ignoreNulls) === 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) { if ("__contentAddressed" in attrs && force(attrs.__contentAddressed) === true) {
throw new Error("ca derivations are not supported"); throw new Error("ca derivations are not supported");
} }

View File

@@ -16,7 +16,6 @@ import type {
} from "./types"; } from "./types";
import { isStringWithContext, isNixPath } from "./types"; import { isStringWithContext, isNixPath } from "./types";
import { force } from "./thunk"; import { force } from "./thunk";
import { getStringValue } from "./string-context";
import { isAttrs, isFunction, typeOf } from "./builtins/type-check"; 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 * Force a value and assert it's a function or functor
* @throws TypeError if value is not a function after forcing * @throws TypeError if value is not a function or functor after forcing
*/ */
export const forceFunction = (value: NixValue): NixFunction => { export const forceFunction = (value: NixValue): NixFunction => {
const forced = force(value); const forced = force(value);
if (!isFunction(forced)) { if (isFunction(forced)) {
throw new TypeError(`Expected function, got ${typeOf(forced)}`);
}
return 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_ir = ctx.get_ir(inner);
let inner_span = inner_ir.span(); let inner_span = inner_ir.span();
code!(buf, "let expr{}=Nix.createThunk(()=>(", slot.0);
inner_ir.compile(ctx, buf);
code!( code!(
buf, buf, ctx;
"),\"expr{} {}:{}:{}\");", "let expr" slot.0 "=Nix.createThunk(()=>(" inner_ir "),"
slot.0, "\"expr" slot.0 " "
ctx.get_current_source().get_name(), ctx.get_current_source().get_name() ":"
usize::from(inner_span.start()), usize::from(inner_span.start()) ":"
usize::from(inner_span.end()) usize::from(inner_span.end())
"\");"
); );
} }
} }