export type VType = | { type: "error" } | { type: "unknown" } | { type: "null" } | { type: "int" } | { type: "string" } | { type: "bool" } | { type: "ref"; subject: VType } | { type: "ref_mut"; subject: VType } | { type: "ptr"; subject: VType } | { type: "ptr_mut"; subject: VType } | { type: "array"; subject: VType } | { type: "struct"; fields: VTypeParam[] } | { type: "fn"; genericParams?: VTypeGenericParam[]; params: VTypeParam[]; returnType: VType; stmtId: number; } | { type: "generic"; param: VTypeGenericParam } | { type: "generic_spec"; subject: VType; genericArgs: GenericArgsMap; }; export type VTypeParam = { ident: string; mut: boolean; vtype: VType; }; export type VTypeGenericParam = { id: number; ident: string; }; export type GenericArgsMap = { [id: number]: VType }; export function vtypesEqual( a: VType, b: VType, generics?: GenericArgsMap, ): boolean { if ( ["error", "unknown", "null", "int", "string", "bool"] .includes(a.type) && a.type === b.type ) { return true; } if (a.type === "ref" && b.type === "ref") { return vtypesEqual(a.subject, b.subject, generics); } if (a.type === "ref_mut" && b.type === "ref_mut") { return vtypesEqual(a.subject, b.subject, generics); } if (a.type === "ptr" && b.type === "ptr") { return vtypesEqual(a.subject, b.subject, generics); } if (a.type === "ptr_mut" && b.type === "ptr_mut") { return vtypesEqual(a.subject, b.subject, generics); } if (a.type === "array" && b.type === "array") { return vtypesEqual(a.subject, b.subject, generics); } if (a.type === "struct" && b.type === "struct") { if (a.fields.length !== b.fields.length) { return false; } const match = a.fields .map((af) => ({ ident: af.ident, af, bf: b.fields.find((bf) => bf.ident === af.ident), })); if (match.some((m) => m.bf === undefined)) { return false; } if (match.some((m) => !vtypesEqual(m.af.vtype, m.bf!.vtype))) { return false; } return true; } if (a.type === "fn" && b.type === "fn") { if (a.params.length !== b.params.length) { return false; } for (let i = 0; i < a.params.length; ++i) { if (!vtypesEqual(a.params[i].vtype, b.params[i].vtype)) { return false; } } return vtypesEqual(a.returnType, b.returnType, generics); } if (a.type === "generic" && b.type === "generic") { return a.param.id === b.param.id; } if ( (a.type === "generic" || b.type === "generic") && generics !== undefined ) { if (generics === undefined) { throw new Error(); } const generic = a.type === "generic" ? a : b; const concrete = a.type === "generic" ? b : a; const genericType = extractGenericType(generic, generics); return vtypesEqual(genericType, concrete, generics); } return false; } export function extractGenericType( generic: VType, generics: GenericArgsMap, ): VType { if (generic.type !== "generic") { return generic; } if (!(generic.param.id in generics)) { throw new Error("generic not found (not supposed to happen)"); } return generics[generic.param.id]; } export function vtypeToString(vtype: VType): string { if ( ["error", "unknown", "null", "int", "string", "bool"] .includes(vtype.type) ) { return vtype.type; } if (vtype.type === "ref") { return `&${vtypeToString(vtype.subject)}`; } if (vtype.type === "ref_mut") { return `&mut ${vtypeToString(vtype.subject)}`; } if (vtype.type === "ptr") { return `*${vtypeToString(vtype.subject)}`; } if (vtype.type === "ptr_mut") { return `*mut ${vtypeToString(vtype.subject)}`; } if (vtype.type === "array") { return `[${vtypeToString(vtype.subject)}]`; } if (vtype.type === "struct") { const fields = vtype.fields .map((field) => `${field.ident}: ${vtypeToString(field.vtype)}`) .join(", "); return `struct { ${fields} }`; } if (vtype.type === "fn") { const paramString = vtype.params.map((param) => `${param.ident}: ${vtypeToString(param.vtype)}` ) .join(", "); return `fn(${paramString}) -> ${vtypeToString(vtype.returnType)}`; } if (vtype.type === "generic") { return `generic`; } throw new Error(`unhandled vtype '${vtype.type}'`); }