@informalsystems/quint
Version:
Core tool for the Quint specification language
101 lines • 3.7 kB
JavaScript
"use strict";
/* ----------------------------------------------------------------------------------
* Copyright 2024 Informal Systems
* Licensed under the Apache License, Version 2.0.
* See LICENSE in the project root for license information.
* --------------------------------------------------------------------------------- */
Object.defineProperty(exports, "__esModule", { value: true });
exports.NondetChecker = void 0;
/**
* Checking for the misuse of 'nondet', 'oneOf', and 'apalache::generate'.
* Necessary to ensure they are compatible with the exists operator from TLA+.
*
* @author Gabriela Moreira
*
* @module
*/
const IRVisitor_1 = require("../ir/IRVisitor");
const printing_1 = require("../types/printing");
class NondetChecker {
constructor(table) {
this.types = new Map();
this.lastDefQualifier = 'def';
this.errors = [];
this.table = table;
}
/**
* Checks declarations for misuse of 'nondet', 'oneOf', and 'apalache::generate'.
*
* @param types - the types of the declarations
* @param declarations - the declarations to check
*
* @returns a list of errors (empty if no errors are found)
*/
checkNondets(types, declarations) {
this.types = types;
declarations.forEach(d => (0, IRVisitor_1.walkDeclaration)(this, d));
return this.errors;
}
enterOpDef(def) {
this.lastDefQualifier = def.qualifier;
const entry = this.table.get(def.id);
if (!entry) {
// A name resolution error should have been reported already
return;
}
if (def.qualifier === 'nondet' && entry.depth === 0) {
this.errors.push({
code: 'QNT206',
message: `'nondet' can only be used inside actions, not at the top level`,
reference: def.id,
data: {},
});
}
}
enterApp(app) {
if (app.opcode !== 'oneOf' && app.opcode !== 'apalache::generate') {
// nothing to check
return;
}
if (this.lastDefQualifier !== 'nondet') {
this.errors.push({
code: 'QNT203',
message: `'${app.opcode}' must be used inside a nondet definition`,
reference: app.id,
data: {},
});
return;
}
}
enterLet(expr) {
if (expr.opdef.qualifier !== 'nondet') {
return;
}
// body of nondet must be an application of oneOf or apalache::generate
const body = expr.opdef.expr;
if (body.kind !== 'app' || (body.opcode !== 'oneOf' && body.opcode !== 'apalache::generate')) {
this.errors.push({
code: 'QNT204',
message: `the outermost expression in a nondet definition must be either 'oneOf' or 'apalache::generate'`,
reference: body.id,
data: {},
});
}
// if the opdef is nondet, the return type must be bool
const expressionType = this.types.get(expr.expr.id);
if (!expressionType) {
// A type error should have been reported already
return;
}
if (expressionType.type.kind !== 'bool') {
this.errors.push({
code: 'QNT205',
message: `nondet bindings can only be used with boolean expressions, but expression has type: ${(0, printing_1.typeSchemeToString)(expressionType)}`,
reference: expr.id,
data: {},
});
}
}
}
exports.NondetChecker = NondetChecker;
//# sourceMappingURL=NondetChecker.js.map