question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

"In evaluation, the identifier v is either undefined or not an operator" when temporal properties involve operators with Java module overrides

See original GitHub issue

Hi,

I have a spec like below

spec.tla

-------------------------------- MODULE spec --------------------------------

EXTENDS Integers, TLC, Sequences, Json

VARIABLES mainVar

vars == << mainVar >>

Init == mainVar = << 1 >>

Next == mainVar' = << 3 >>

prop ==
  <>(JsonSerialize("eiei.json", mainVar) /\ Head(mainVar) >= 1)

Spec == Init /\ [][Next]_vars

=============================================================================

-------------------------------- CONFIG spec --------------------------------

INIT
   Init

NEXT
   Next

PROPERTY
  prop

=============================================================================


When I run it with TLC, it gives me

...
Semantic processing of module TLC
Semantic processing of module Json
Semantic processing of module spec
Starting... (2021-04-14 23:29:09)
Error: In evaluation, the identifier mainVar is either undefined or not an operator.
line 14, col 20 to line 14, col 26 of module spec
Finished in 00s at (2021-04-14 23:29:09)

Originally I have been using a custom overridden operator with TLAPlusOperator (which just returns TRUE and it works fine when not in use by a temporal property) and it was giving me the same error, I’m just using JsonSerialize because it’s already embedded. Removing the JsonSerialize and letting Head alone works (although Head also is an overridden operator, but it uses a different mechanism other that `TLAPlusOperator).


Use the toolbox or run the spec from the CLI with

$ java -cp $SOME_FOLDER/tla2tools.jar tlc2.TLC spec.tla -config spec.tla

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:11 (11 by maintainers)

github_iconTop GitHub Comments

1reaction
lemmycommented, Apr 17, 2021

Root cause

A non-zero arity operator with constant-level right-hand side, whose (left-hand side) arguments do not appear on the RHS, is constant-level. Consider the operator Op:

EXTENDS Naturals
VARIABLE v

Op(a,b) == TRUE

Init == v = 0

Next == UNCHANGED v

Prop == <>Op(v, 42)

Inv == Op(v, 42)

Op is clearly constant-level even if Op is evaluated with some or all of its arguments equal to variables.

This fact causes a bug (see above), iff the operator appears in a (temporal) property and has a Java module override:

In evaluation, the identifier v is either undefined or not an operator.

Op is accepted by safety checking (Inv) because safety is more liberal compared to liveness checking when it comes to Op’s level. In other words, safety happily checks Spec => []TRUE.

Scope

Below is a list of operators, from the (extended) list of standard operators, that are affected when evaluated with state-level arguments in a (liveness) property:

  • TLC!Print (when out argument has state-level)
  • TLC!PrintT
  • TLC!Assert (out state-level)
  • TLC!TLCGet
  • TLC!TLCSet

Not affected because ops use @Evaluation or lack a Java module override:

  • TLCExt!Trace
  • TLCExt!TLCDefer
  • Toolbox!_TETrace
  • Toolbox!_TEPosition

Likely affected CommunityModules modules:

  • CSV.tla
  • IOUtils.tla
  • SVG.tla (SVGElemToString, NodeOfRingNetwork)
  • ShiViz.tla (requries further investigation)
  • Json.tla (deprecated)
  • TLCExt.tla (deprecated)

Possible fixes:

  1. Don’t fix
    • Except for Print, PrintT, and, perhaps, TLCGet, most operators usually don’t appear in liveness properties. However, TLCGet has been discussed in the scope of trace evaluation on the command-line. Also, the liveness property might be a behavior spec, and in behavior specs, it would be less surprising to find e.g. a PrintT(var). However, the level-checking in then does not take the code path that triggers the bug.
    • One more inconsistency/pitfall => Print a proper warning instead of the bogus one shown so far
  2. Come up with proper TLA+ definitions for all operators
    • The definition of PrintT is proper TLA+. Rather, PrintT should be seen as an operator with side-effects outside of the TLA+ formalism
    • Hack: -redefine PrintT(val) == val = val, TLCGet(i) == CHOOSE n : i = i, TLCGet(i) == CHOOSE n : i = i
  3. Dynamically determine the operator’s level during liveness checking as implemented in the hack below
    • Performance overhead
    • Probably create more problems than it solves
  4. Fix up/bump the operator’s level while loading the module override
    • Works for all operators even user-provided ones
    • Chance to increase level even when not needed (unless the Java code is analyzed; Let’s not go there!)
    • Risk, because it fiddles with level-checking and affects code outside of liveness checking

diff --git a/tlatools/org.lamport.tlatools/src/tlc2/tool/Specs.java b/tlatools/org.lamport.tlatools/src/tlc2/tool/Specs.java
index 4ff9e63c9..6bdca7d86 100644
--- a/tlatools/org.lamport.tlatools/src/tlc2/tool/Specs.java
+++ b/tlatools/org.lamport.tlatools/src/tlc2/tool/Specs.java
@@ -46,8 +46,14 @@ public abstract class Specs {
        public static int getLevel(LevelNode expr, Context c)
        {
            HashSet<SymbolNode> lpSet = expr.getLevelParams();
-           if (lpSet.isEmpty())
-               return expr.getLevel();
+           if (lpSet.isEmpty()) {
+               int level = expr.getLevel();
+               HashSet<SymbolNode> allParams = expr.getAllParams();
+               for (SymbolNode p : allParams) {
+                               level = Math.max(level, p.getLevel());
+                       }
+                       return level;
+           }
        
            int level = expr.getLevel();
            Iterator<SymbolNode> iter = lpSet.iterator();
1reaction
lemmycommented, Apr 16, 2021

Please try loading your module override that fails with @TLAPlusOperator through the old mechanism: Place it in the default package, align the Java class file name with the module name, and the operator name with the (static) method name. E.g for a constant-level operator defined in module M with name Op(a,b,c):

// no package => default package
public class M
public static Value  Op(Value a, Value b, Value c) {
   return BoolValue.ValTrue;
}
Read more comments on GitHub >

github_iconTop Results From Across the Web

Overriding operator which is used in a temporal property
Error : In evaluation, the identifier mainVar is either undefined or not an operator. I'm doing this for a personal project and I...
Read more >
In evaluation, the identifier messagesSend is either undefined ...
"In evaluation, the identifier messagesSend is either undefined or not an operator." Can someone help me along explaining my error?
Read more >
in works, while \subseteq gives a "identifier undefined" error
In evaluation, the identifier members is either undefined or not an operator. The error points to the Init line.
Read more >
mku · GitHub
Trace expression evaluation fails for a trace where the initial state ... (beginner) Trying to write a new module override #713 opened by...
Read more >
Specifying Concurrent Systems with TLA+ - Leslie Lamport
properties, which can be specified with almost no temporal logic, are all that. most engineers will need to know about.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found