UNPKG

@informalsystems/quint

Version:

Core tool for the Quint specification language

222 lines 12.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const mocha_1 = require("mocha"); const chai_1 = require("chai"); const ir_1 = require("../builders/ir"); const printing_1 = require("../../src/effects/printing"); const errorTree_1 = require("../../src/errorTree"); const inferrer_1 = require("../../src/effects/inferrer"); const util_1 = require("../util"); const quintIr_1 = require("../../src/ir/quintIr"); (0, mocha_1.describe)('inferEffects', () => { const baseDefs = ['const N: int', 'const S: Set[int]', 'var x: int']; function inferEffectsForDefs(defs) { const text = `module wrapper { ${baseDefs.concat(defs).join('\n')} }`; const { modules, table } = (0, util_1.parseMockedModule)(text); const inferrer = new inferrer_1.EffectInferrer(table); return inferrer.inferEffects(modules[0].declarations); } function effectForDef(defs, effects, defName) { const module = (0, ir_1.buildModuleWithDecls)(baseDefs.concat(defs)); const result = module.declarations.find(decl => (0, quintIr_1.isDef)(decl) && decl.name === defName); if (!result) { throw new Error(`Could not find def with name ${defName}`); } return (0, printing_1.effectSchemeToString)(effects.get(result.id)); } (0, mocha_1.it)('infers simple operator effect', () => { const defs = [`def a(p) = x' = p`]; const [errors, effects] = inferEffectsForDefs(defs); const expectedEffect = "∀ v0 . (Read[v0]) => Read[v0] & Update['x']"; chai_1.assert.isEmpty(errors, `Should find no errors, found: ${[...errors.values()].map(errorTree_1.errorTreeToString)}`); chai_1.assert.deepEqual(effectForDef(defs, effects, 'a'), expectedEffect); }); (0, mocha_1.it)('infers application of multiple arity opertors', () => { const defs = ['def a(p) = and(p, x)', 'def b(p) = and(p, 1, 2)']; const [errors, effects] = inferEffectsForDefs(defs); chai_1.assert.isEmpty(errors, `Should find no errors, found: ${[...errors.values()].map(errorTree_1.errorTreeToString)}`); chai_1.assert.deepEqual(effectForDef(defs, effects, 'a'), "∀ v0, v1 . (Read[v0] & Temporal[v1]) => Read[v0, 'x'] & Temporal[v1]"); chai_1.assert.deepEqual(effectForDef(defs, effects, 'b'), '∀ v0, v1 . (Read[v0] & Temporal[v1]) => Read[v0] & Temporal[v1]'); }); (0, mocha_1.it)('infers references to operators', () => { const defs = ['def a(p) = foldl(x, p, iadd)']; const [errors, effects] = inferEffectsForDefs(defs); const expectedEffect = "∀ v0, v1 . (Read[v0] & Temporal[v1]) => Read[v0, 'x'] & Temporal[v1]"; chai_1.assert.isEmpty(errors, `Should find no errors, found: ${[...errors.values()].map(errorTree_1.errorTreeToString)}`); chai_1.assert.deepEqual(effectForDef(defs, effects, 'a'), expectedEffect); }); (0, mocha_1.it)('infers references to user-defined operators', () => { const defs = ['def a(p) = def my_add = iadd { foldl(x, p, my_add) }']; const [errors, effects] = inferEffectsForDefs(defs); const expectedEffect = "∀ v0, v1 . (Read[v0] & Temporal[v1]) => Read[v0, 'x'] & Temporal[v1]"; chai_1.assert.isEmpty(errors, `Should find no errors, found: ${[...errors.values()].map(errorTree_1.errorTreeToString)}`); chai_1.assert.deepEqual(effectForDef(defs, effects, 'a'), expectedEffect); }); (0, mocha_1.it)('infers effects for operators defined with let-in', () => { const defs = ['val b = val m = x { m }']; const [errors, effects] = inferEffectsForDefs(defs); const expectedEffect = "Read['x']"; chai_1.assert.isEmpty(errors, `Should find no errors, found: ${[...errors.values()].map(errorTree_1.errorTreeToString)}`); chai_1.assert.deepEqual(effectForDef(defs, effects, 'b'), expectedEffect); }); (0, mocha_1.it)('infers pure effect for literals and value constants', () => { const defs = ['val b = N + 1']; const [errors, effects] = inferEffectsForDefs(defs); const expectedEffect = 'Pure'; chai_1.assert.isEmpty(errors, `Should find no errors, found: ${[...errors.values()].map(errorTree_1.errorTreeToString)}`); chai_1.assert.deepEqual(effectForDef(defs, effects, 'b'), expectedEffect); }); (0, mocha_1.it)('infers arrow effect for operator constants', () => { const defs = ['const MyOp: int => int', 'val b = MyOp(x)']; const [errors, effects] = inferEffectsForDefs(defs); const expectedEffect = "Read['x']"; chai_1.assert.isEmpty(errors, `Should find no errors, found: ${[...errors.values()].map(errorTree_1.errorTreeToString)}`); chai_1.assert.deepEqual(effectForDef(defs, effects, 'b'), expectedEffect); }); (0, mocha_1.it)('handles underscore', () => { const defs = ['val b = N.map(_ => 1)']; const [errors, effects] = inferEffectsForDefs(defs); const expectedEffect = 'Pure'; chai_1.assert.isEmpty(errors, `Should find no errors, found: ${[...errors.values()].map(errorTree_1.errorTreeToString)}`); chai_1.assert.deepEqual(effectForDef(defs, effects, 'b'), expectedEffect); }); (0, mocha_1.it)('infers polymorphic high order operators', () => { const defs = ['def a(g, p) = g(p)']; const [errors, effects] = inferEffectsForDefs(defs); const expectedEffect = '∀ e0, e1 . ((e0) => e1, e0) => e1'; chai_1.assert.isEmpty(errors, `Should find no errors, found: ${[...errors.values()].map(errorTree_1.errorTreeToString)}`); chai_1.assert.deepEqual(effectForDef(defs, effects, 'a'), expectedEffect); }); (0, mocha_1.it)('infers monomorphic high order operators', () => { const defs = ['def a(g, p) = g(p) + g(not(p))']; const [errors, effects] = inferEffectsForDefs(defs); const expectedEffect = '∀ v0, v1, v2, v3 . ((Read[v0] & Temporal[v1]) => Read[v2] & Temporal[v3], Read[v0] & Temporal[v1]) => Read[v2] & Temporal[v3]'; chai_1.assert.isEmpty(errors, `Should find no errors, found: ${[...errors.values()].map(errorTree_1.errorTreeToString)}`); chai_1.assert.deepEqual(effectForDef(defs, effects, 'a'), expectedEffect); }); (0, mocha_1.it)('keeps track of substitutions with nested defs', () => { const defs = [ 'pure def a(p) = and{' + ' val b = p + 1' + ' p + b > 0,' + ' val c = p + 2' + ' p + c > 0,' + ' p > 0,' + '}', ]; const [errors, effects] = inferEffectsForDefs(defs); const expectedEffect = '∀ v0, v1 . (Read[v0] & Temporal[v1]) => Read[v0] & Temporal[v1]'; chai_1.assert.isEmpty(errors, `Should find no errors, found: ${[...errors.values()].map(errorTree_1.errorTreeToString)}`); chai_1.assert.deepEqual(effectForDef(defs, effects, 'a'), expectedEffect); }); (0, mocha_1.it)('keeps track of substitutions with lambdas and applications', () => { const defs = [ `pure def MinBy(__set: Set[a], __f: a => int, __i: a): a = { __set.fold( __i, (__m, __e) => if(__f(__m) < __f(__e)) {__m } else {__e} ) }`, ]; const [errors, effects] = inferEffectsForDefs(defs); const expectedEffect = '∀ v0, v1 . (Read[v0], (Read[v0]) => Read[v1], Read[v0]) => Read[v0, v1]'; chai_1.assert.isEmpty(errors, `Should find no errors, found: ${[...errors.values()].map(errorTree_1.errorTreeToString)}`); chai_1.assert.deepEqual(effectForDef(defs, effects, 'MinBy'), expectedEffect); }); (0, mocha_1.it)('regression on #1091', () => { const defs = [ 'var channel: int', `action CoolAction(boolean: bool): bool = any { all { boolean, channel' = channel }, all { not(boolean), channel' = channel } }`, ]; const [errors, effects] = inferEffectsForDefs(defs); const expectedEffect = "∀ v0 . (Read[v0]) => Read[v0, 'channel'] & Update['channel']"; chai_1.assert.isEmpty(errors, `Should find no errors, found: ${[...errors.values()].map(errorTree_1.errorTreeToString)}`); chai_1.assert.deepEqual(effectForDef(defs, effects, 'CoolAction'), expectedEffect); }); (0, mocha_1.it)('avoids invalid cyclical binding error (regression on #1356)', () => { const defs = [ `pure def foo(s: int, g: int => int): int = { val r = if (true) s else g(s) g(r) }`, ]; const [errors, _] = inferEffectsForDefs(defs); chai_1.assert.isEmpty(errors, `Should find no errors, found: ${[...errors.values()].map(errorTree_1.errorTreeToString)}`); }); (0, mocha_1.it)('returns error when operator signature is not unifiable with args', () => { const defs = [`def a = S.map(p => x' = p)`]; const [errors] = inferEffectsForDefs(defs); errors.forEach(v => chai_1.assert.deepEqual(v, { children: [ { children: [ { children: [ { children: [ { children: [], location: "Trying to unify entities ['x'] and []", message: 'Expected [x] and [] to be the same', }, ], location: "Trying to unify Read[_v4] & Temporal[_v5] and Update['x']", }, ], location: "Trying to unify (Pure) => Read[_v4] & Temporal[_v5] and (Read[_v1]) => Read[_v1] & Update['x']", }, ], location: "Trying to unify (Read[_v2] & Temporal[_v3], (Read[_v2] & Temporal[_v3]) => Read[_v4] & Temporal[_v5]) => Read[_v2, _v4] & Temporal[_v3, _v5] and (Pure, (Read[_v1]) => Read[_v1] & Update['x']) => _e1", }, ], location: 'Trying to infer effect for operator application in map(S, ((p) => assign(x, p)))', })); }); (0, mocha_1.it)('returns error when lambda returns an operator', () => { const defs = ['pure def f(p) = p', 'pure def myOp = (_) => f']; const [errors] = inferEffectsForDefs(defs); chai_1.assert.deepEqual([...errors.values()][0], { children: [], location: 'Inferring effect for f', message: 'Result cannot be an operator', }); }); (0, mocha_1.it)('returns error when `match` branches update different variables', () => { const defs = ['type Result = | Some(int) | None', "val foo = match Some(1) { | Some(n) => x' = n | None => true }"]; const [errors] = inferEffectsForDefs(defs); chai_1.assert.deepEqual([...errors.values()][0].children[0].children[0].children[0].children[0], { children: [], location: "Trying to unify entities ['x'] and []", message: 'Expected [x] and [] to be the same', }); }); (0, mocha_1.it)('differentiates variables from different instances', () => { const baseDefs = ['const N: int', 'const S: Set[int]', 'var x: int']; const text = ` module base { ${baseDefs.join('\n')} } module wrapper { import base(N=1) as B1 import base(N=2) as B2 val a = B1::x + B2::x }`; const { modules, table } = (0, util_1.parseMockedModule)(text); const inferrer = new inferrer_1.EffectInferrer(table); inferrer.inferEffects(modules[0].declarations); const [errors, effects] = inferrer.inferEffects(modules[1].declarations); chai_1.assert.isEmpty(errors, `Should find no errors, found: ${[...errors.values()].map(errorTree_1.errorTreeToString)}`); const def = modules[1].declarations.find(decl => (0, quintIr_1.isDef)(decl) && decl.name === 'a'); const expectedEffect = "Read['wrapper::B1::x', 'wrapper::B2::x']"; chai_1.assert.deepEqual((0, printing_1.effectSchemeToString)(effects.get(def.id)), expectedEffect); }); }); //# sourceMappingURL=inferrer.test.js.map