-
Notifications
You must be signed in to change notification settings - Fork 38
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Checks for misuse of nondet
and oneOf
#1431
Changes from all commits
0105c97
d0d7edf
71cd1cf
fef5ea7
5cead19
969fcbb
3ba143b
53b073c
cf47cc0
6fc1738
69b8280
371ced4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
/* ---------------------------------------------------------------------------------- | ||
* Copyright 2024 Informal Systems | ||
* Licensed under the Apache License, Version 2.0. | ||
* See LICENSE in the project root for license information. | ||
* --------------------------------------------------------------------------------- */ | ||
|
||
/** | ||
* Checking for the misuse of 'nondet' and 'oneOf'. Necessary to ensure they are | ||
* compatible with the exists operator from TLA+. | ||
* | ||
* @author Gabriela Moreira | ||
* | ||
* @module | ||
*/ | ||
|
||
import { IRVisitor, walkDeclaration } from '../ir/IRVisitor' | ||
import { OpQualifier, QuintApp, QuintDeclaration, QuintLet, QuintOpDef } from '../ir/quintIr' | ||
import { LookupTable } from '../names/base' | ||
import { QuintError } from '../quintError' | ||
import { TypeScheme } from '../types/base' | ||
import { typeSchemeToString } from '../types/printing' | ||
|
||
export class NondetChecker implements IRVisitor { | ||
private table: LookupTable | ||
private types: Map<bigint, TypeScheme> = new Map() | ||
private lastDefQualifier: OpQualifier = 'def' | ||
|
||
private errors: QuintError[] = [] | ||
|
||
constructor(table: LookupTable) { | ||
this.table = table | ||
} | ||
|
||
/** | ||
* Checks declarations for misuse of 'nondet' and 'oneOf'. | ||
* | ||
* @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: Map<bigint, TypeScheme>, declarations: QuintDeclaration[]): QuintError[] { | ||
this.types = types | ||
|
||
declarations.forEach(d => walkDeclaration(this, d)) | ||
return this.errors | ||
} | ||
|
||
enterOpDef(def: QuintOpDef) { | ||
this.lastDefQualifier = def.qualifier | ||
|
||
if (def.qualifier === 'nondet' && this.table.get(def.id)!.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: QuintApp) { | ||
if (app.opcode !== 'oneOf') { | ||
// nothing to check | ||
return | ||
} | ||
|
||
if (this.lastDefQualifier !== 'nondet') { | ||
this.errors.push({ | ||
code: 'QNT203', | ||
message: `'oneOf' must be used inside a nondet definition`, | ||
reference: app.id, | ||
data: {}, | ||
}) | ||
return | ||
} | ||
} | ||
|
||
enterLet(expr: QuintLet) { | ||
if (expr.opdef.qualifier !== 'nondet') { | ||
return | ||
} | ||
|
||
// body of nondet must be an application of oneOf | ||
const body = expr.opdef.expr | ||
if (body.kind !== 'app' || body.opcode !== 'oneOf') { | ||
this.errors.push({ | ||
code: 'QNT204', | ||
message: `'oneOf' must be the outermost expression in a nondet definition`, | ||
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.type.kind !== 'bool') { | ||
this.errors.push({ | ||
code: 'QNT205', | ||
message: `nondet bindings can only be used with boolean expressions, but expression has type: ${typeSchemeToString( | ||
expressionType | ||
)}`, | ||
reference: expr.id, | ||
data: {}, | ||
}) | ||
} | ||
} | ||
} |
Large diffs are not rendered by default.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Large diffs are not rendered by default.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Turns out all that was stopping this from working with Apalache was a hidden nondet issue :)