@informalsystems/quint
Version:
Core tool for the Quint specification language
173 lines • 7.45 kB
JavaScript
;
/* ----------------------------------------------------------------------------------
* Copyright 2024 Informal Systems
* Licensed under the Apache License, Version 2.0.
* See LICENSE in the project root for license information.
* --------------------------------------------------------------------------------- */
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TypeApplicationResolver = void 0;
/**
* Resolution of type application
*
* @author Shon Feder
*
* @module
*/
const errorTree_1 = require("../errorTree");
const FreshVarGenerator_1 = require("../FreshVarGenerator");
const IRprinting_1 = require("../ir/IRprinting");
const IRTransformer_1 = require("../ir/IRTransformer");
const util_1 = require("../util");
const aliasInliner_1 = require("./aliasInliner");
const substitutions_1 = require("./substitutions");
const assert_1 = __importDefault(require("assert"));
/** Resolves all type applications in an IR object */
class TypeApplicationResolver {
constructor(table) {
// Errors found during type application resolution
this.errors = new Map();
this.table = table;
this.freshVarGenerator = new FreshVarGenerator_1.FreshVarGenerator();
this.table.forEach((def, id) => {
const resolvedLookupDef = (0, IRTransformer_1.transformLookupDefinition)(this, def);
this.table.set(id, resolvedLookupDef);
});
}
resolveTypeApplications(decls) {
const resolvedDecls = decls.map(decl => (0, IRTransformer_1.transformDeclaration)(this, decl));
const errors = this.errors;
return [errors, resolvedDecls];
}
exitType(t) {
return this.resolveTypeApplicationsForType(t);
}
// Transforms `t` by resolving all the type applications in all its sub-terms
//
// E.g., given
//
// type Foo[a, b] = (a, b)
// type Bar[x, y] = {i: x, j: y}
//
//
// resolveTypeApplicationsForType(Foo[a, {f: Bar[int, str]}]) = (a, {f: {i: int, j: str}})
resolveTypeApplicationsForType(t) {
const f = x => (x.kind !== 'app' ? x : this.resolveTypeApp(x));
return mapType(f, t);
}
resolveTypeApp(t) {
// Ensured by parsing
(0, assert_1.default)(t.id, `invalid IR node: type application ${(0, IRprinting_1.typeToString)(t)} has no id`);
// Ensured by parsing
(0, assert_1.default)(t.ctor.id, `invalid IR node: type constructor ${t.ctor.name} in type application ${(0, IRprinting_1.typeToString)(t)} id ${t.id} has no id`);
const typeDef = this.table.get(t.ctor.id);
// Ensured by name resolution
(0, assert_1.default)(typeDef, `invalid IR reference: type constructor ${t.ctor.name} with id ${t.ctor.id} has no type definition`);
// Ensured by the grammar and by name resolution
(0, assert_1.default)(typeDef.kind === 'typedef' && typeDef.type, `invalid kind looked up for constructor of type application with id ${t.ctor.id} `);
const { params, type } = this.freshTypeFromDef(typeDef);
// NOTE: Early exit on error
// Check for arity mismatch in type application
if (params.length !== t.args.length) {
const ctorMsg = (0, IRprinting_1.typeToString)(t.ctor);
const typeArgsMsg = t.args.map(IRprinting_1.typeToString).join(', ');
const manyOrFew = params.length > t.args.length ? 'few' : 'many';
const err = (0, errorTree_1.buildErrorLeaf)(`applying type constructor ${ctorMsg} to arguments ${typeArgsMsg}`, `too ${manyOrFew} arguments supplied: ${ctorMsg} only accepts ${params.length} parameters`);
this.errors.set(t.id, err);
return t;
}
// Substitute the type `args` for each corresponding fresh variable
const subs = (0, util_1.zip)(params, t.args).flatMap(([param, arg]) => {
const resolvedArg = (0, aliasInliner_1.resolveAlias)(this.table, arg);
const s = [
{
kind: 'type',
name: param.name,
value: resolvedArg,
},
];
// If the argument is a record or tuple, it might be that we are trying to
// instantiate a row variable, i.e.
// type Foo[r] = { x: int | r }
// the type app Foo[{ y: str }] should result in { x: int, y: str }
if (resolvedArg.kind == 'rec' || resolvedArg.kind == 'tup') {
s.push({
kind: 'row',
name: param.name,
value: resolvedArg.fields,
});
}
// And similarly, if the argument is a variable itself
// type Foo[r] = { x: int | r }
// type Bar[s] = Foo[s] should be resolved to Bar[s] = { x: int | s }
if (resolvedArg.kind == 'var') {
s.push({
kind: 'row',
name: param.name,
value: { kind: 'var', name: resolvedArg.name },
});
}
return s;
});
return (0, substitutions_1.applySubstitution)(this.table, subs, type);
}
// Given a type definition, extract the type it is defined by (with all type
// parameters replaced with fresh variables) and a list of params giving the
// fresh type variables in the order corresponding to the params they
// replaced in the type declaration.
//
// E.g., the type definition
//
// type Result[ok, err] = Ok(ok) | Err(err)
//
// Will produce the result
//
// { params: [fresh_ok, fresh_err],
// type: (Ok(fresh_ok) | Err(fresh_err))
// }
freshTypeFromDef(typeDef) {
if (!typeDef.params || typeDef.params.length === 0) {
return { params: [], type: typeDef.type };
}
// Coordinates parameter names with their corresponding fresh variables
const varsMap = new Map(typeDef.params.map(param => [param, this.freshVarGenerator.freshVar(param)]));
// Parsing guarantees that every variable in a type def is in the params
const type = mapTypeVarNames(n => varsMap.get(n) ?? n, typeDef.type);
const freshParamNames = [...varsMap.values()];
const params = freshParamNames.map(name => ({ kind: 'var', name }));
return { type, params };
}
}
exports.TypeApplicationResolver = TypeApplicationResolver;
// Map type variable names according to `f`
function mapTypeVarNames(f, t) {
const transformer = new TypeVariableNameMapper(f);
return (0, IRTransformer_1.transformType)(transformer, t);
}
class TypeVariableNameMapper {
constructor(f) {
this.mapper = f;
}
enterVarType(t) {
return { ...t, name: this.mapper(t.name) };
}
enterRow(r) {
return r.kind === 'var' ? { ...r, name: this.mapper(r.name) } : r;
}
}
// Transform `t`, and all its subterms, by `f`
function mapType(f, t) {
const transformer = new TypeMapper(f);
return (0, IRTransformer_1.transformType)(transformer, t);
}
class TypeMapper {
constructor(f) {
this.mapper = f;
}
enterType(t) {
return this.mapper(t);
}
}
//# sourceMappingURL=typeApplicationResolution.js.map