UNPKG

@informalsystems/quint

Version:

Core tool for the Quint specification language

173 lines 7.45 kB
"use strict"; /* ---------------------------------------------------------------------------------- * 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